Giter Club home page Giter Club logo

silk's Introduction

Silk logo

silk Build Status Go Report Card

Markdown based document-driven web API testing.

Learn more

Video of Mat Ryer speaking about Silk

(VIDEO) Watch the talk about Silk (with slides) or read about Silk in this blog post.

Example Silk test file

Markdown API

Tests are made up of documents written in Markdown.

  • # Group - Top level headings represent groups of requests
  • ## GET /path - Second level headings represent a request
  • Code blocks with three back tics represent bodies
  • * Field: value - Lists describe headers and assertions
  • * ?param=value - Request parameters
  • --- seperators break requests from responses
  • Comments (starting with //) allow you to capture variables
  • Plain text is ignored to allow you to add documentation
  • Inline back tics are ignored and are available for formatting

Document structure

A document is made up of:

  • A request
  • --- seperator
  • Assertions

Requests

A request starts with ## and must have an HTTP method, and a path:

## METHOD /path

Examples include:

## GET /people

## POST /people/1/comments

## DELETE /people/1/comments/2

Request body (optional)

To specify a request body (for example for POST requests) use a codeblock using backtics (```):

```
{"name": "Silk", "release_year": 2016}
```

Request headers (optional)

You may specify request headers using lists (prefixed with *):

* Content-Type: "application/json"
* X-Custom-Header: "123"

Request parameters (optional)

Adding parameters to the path (like GET /path?q=something) can be tricky, especially when you consider escaping etc. To address this, Silk supports parameters like lists:

* ?param=value

The parameters will be correctly added to the URL path before the request is made.

Cookies

Setting cookies on a request can be done using the HTTP header pattern:

* Cookie: "key=value"

Assertions

Following the --- separator, you can specify assertions about the response. At a minimum, it is recommended that you assert the status code to ensure the request succeeded:

* Status: 200

You may also specify response headers in the same format as request headers:

* Content-Type: "application/json"
* X-MyServer-Version: "v1.0"

If any of the headers do not match, the test will fail.

Capturing data

Silk allows you to capture values at the point of asserting them and reuse them in future requests and assertions. To capture a value, include a comment on the line that mentions a {placeholder}:

* Data.UserID: /.*/ // The user's unique {id}.

The value from UserID (e.g. 123) will be stored in a variable called id, and you can refer to it later:

## GET /users/{id}

The above would be a request to GET /users/123.

  • Captured values are only available when assertions are successful

Environment variables

You can access environment variables inside Silk tests using the {$NAME} format, where NAME is the environment name.

Asserting cookies

To assert that a cookie is present in a response, make a regex assertion against the Set-Cookie HTTP header:

* Set-Cookie: /key=value/
  • All cookie strings are present in a single Set-Cookie seperated by a pipe character.

Validating data

You can optionally include a verbatim body using code blocks surrounded by three back tics. If the response body does not exactly match, the test will fail:

```
Hello world!
```

You can flag expected response bodies as json directly after the three back tics. This will assert that the actual response contains the same value for each expected key (recursively) allowing for differences in whitespace and ordering as well as being lenient towards additional (unexpected) keys in the response.

```json
{
    "id": 1,
    "release_year": 2016,
    "name": "Silk"
}
```

You can use the flag json(strict) to enforce that no additional fields may be present while still allowing for differences in whitespace and key order.

You may also make any number of regex assertions against the body using the Body object:

* Body: /Hello world/
* Body: /This should be found too/
* Body: /and this/

Alternatively, you can specify a list (using *) of data fields to assert accessible via the Data object:

* Status: 201
* Content-Type: "application/json"
* Data.name: "Silk"
* Data.release_year: 2016
* Data.tags[0]: "testing"
* Data.tags[1]: "markdown"
* Data[0].name: "Mat"
* Data[1].name: "David"
  • NOTE: Currenly this feature is only supported for JSON APIs.

Regex

Values may be regex, if they begin and end with a forward slash: /. The assertion will pass if the value (after being turned into a string) matches the regex.

* Status: /^2.{2}$/
* Content-Type: /application/json/

The above will assert that:

  • The status looks like 2xx, and
  • The Content-Type contains application/json

Command line

The silk command runs tests against an HTTP endpoint.

Usage:

silk -silk.url="{endpoint}" {testfiles...}
  • {endpoint} the endpoint URL (e.g. http://localhost:8080)
  • {testfiles} list of test files (e.g. ./testfiles/one.silk.md ./testfiles/two.silk.md)

Notes:

  • Omit trailing slash from endpoint
  • {testfiles} can include a pattern (e.g. /path/*.silk.md) as this is expended by most terminals to a list of matching files

Golang

Silk is written in Go and integrates seamlessly into existing testing tools and frameworks. Import the runner package and use RunGlob to match many test files:

package project_test

import (
  "testing"
  "github.com/matryer/silk/runner"
)

func TestAPIEndpoint(t *testing.T) {
  // start a server
  s := httptest.NewServer(yourHandler)
  defer s.Close()

  // run all test files
  runner.New(t, s.URL).RunGlob(filepath.Glob("../testfiles/failure/*.silk.md"))
}

Credit

  • Special thanks to @dahernan for his contributions and criticisms of Silk
  • Silk logo by Chris Ryer

silk's People

Contributors

antekresic avatar buddhamagnet avatar dahernan avatar felixlam avatar katre avatar matryer avatar rafavisa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

silk's Issues

Postman export support

Hey found silk quite interesting! I'm looking for to give a try in my next microservice project.

By the way while reading i was thinking in postman as far i know several people using for QA and test (not automated) purpose.

So i was think if make sense to add support for that in this project, if is that something that you are willing for to have into core, or might be some a part, a command that we run pointing the markdown that generates that?

I'm asking because i was thinking in give a try and implement that. 🍻

Support formatted JSON in response body assertions

For a large bodies it'd be much user-friendlier to support formatted JSON in the body assert field. Currently if I have an assertion:

===

'''
{
  "resource": "api_status",
  "body": {
    "blah": "blah"
  }
}
'''

and my API returns {"resource":"api_status","body":{"blah":"blah"}}, I get "body doesn't match" error.

Missing package when building : `github.com/matryer/m`

Hi Mat,

i'm new to go so this may certainly be a problem between the chair and the keyboard, but when i try to go install github.com/matryer/silk from the freshly cloned repo, i get this error :

silk/runner/run.go:17:2: cannot find package "github.com/matryer/m" in any of:
    /usr/local/Cellar/go/1.7.1/libexec/src/github.com/matryer/m (from $GOROOT)
    /Users/sgaide/dev/go/src/github.com/matryer/m (from $GOPATH)

i can find no m package in your github repos either, am i missing something ?

thanks for any help,

Sebastien

Support for multipart multipart/form-data and file upload

Support multipart data and uploading file as a form parameter in POST and PUT methods.
There can be a folder that contains all the files that will be uploaded while testing, this path can be set as an argument while running silk.
The names of the file mentioned in the markdown could be relative to this folder. We can add a comment after the field that the filed is a file field and it's value is a file path.

name: Mat
CV: CV/mat-cv.pdf // file

x509: certificate signed by unknown authority

When I'm running silk against certificate signed by untrusted root, I got the following error.

> silk -silk.url="https://untrusted-root.badssl.com/" ./**/*.silk.md
silk: running 1 file(s)...
x509: certificate signed by unknown authority
--- FAIL: silk (0.67s)
FAIL

I tried to find a way to skip certification checking in help but couldn't find it. (like option -k in curl)

 -k, --insecure      Allow connections to SSL sites without certs (H)

I think the error was caused by http.DefaultTransport used in Runner.DoRequest. It seems to me that DefaultTransport does not allow a connection to insecure SSL sites.

DoRequest: http.DefaultTransport.RoundTrip,

I found a way to skip it here https://groups.google.com/d/msg/golang-nuts/v5ShM8R7Tdc/I2wyTy1o118J

I'm thinking of adding another option in silk to allow connections to SSL sites without certs. Any idea what the option should be? Is -k ok?

Numerical assertions

Hey, not sure if I'm missing something but is there a way to do numerical assertions like:

  • Data.hits.total: > 0

``clean()`` makes error messages confusing when you expect strings with facing and/or trailing spaces

When you have actual data strings with facing and/or trailing spaces, error messages are confusing.

For example an actual data is " expected" then error message is:

Data[9].title expected string: "expected"  actual string: "expected"  

In this case actual string should be shown with a facing space.

clean() function causes this.

return bytes.Trim(bytes.TrimSpace(b), "`")

Are these Trim callings really necessary?

Basic usage information

Hello, I just cloned the repo. But it is impossible to run anything at all from the files in the repo:

silk -silk.url="{endpoint}" {testfiles...}

as suggested in the README produces "command not found"

Executing build.sh results in "main.go:8:2: cannot find package "github.com/matryer/silk/runner""

What is the correct procedure for running the application? Could you adjust the documentation accordingly?

silk middleware

As a Developer
I want to Record tests to silk files
As I manually test my application.

Concept
Provide a middleware handler(s) with flags to enable a record mode and options. The middleware would generate a silk compatible file including the HTTP method and related request parameters, headers, status and response. Start with the standard library http, then adapt to specific frameworks that have more advanced routers.

Proof of Concept
Handle POST request and response for a form. Produce an acceptable set of silk test document(s) for a series of requests.

Problem with expected JSON

Hi,
First of all I'd like to thank you for such a useful library. It's both simple to use and pretty powerful.
I want to report an issue I've been facing using Silk. I've discovered it adding a property to the JSON I'm expecting in the response.
Here is the JSON which works:

{
    "is_complete": true,
    "user": {
        "id": 1,
        "name": "Juan Carlos",
        "email": "[email protected]",
        "phone": "541122334455",
        "gender": "male",
        "gender_of_interest": "women",
        "birthday": "1990-02-15T00:00:00Z",
        "height": 170,
        "job_title": "Software Engineer",
        "company": "Whim",
        "school": "UADE",
        "degree": {
            "id": 3,
            "description": "Bachelor's"
        },
        "descriptors": [
            {
                "id": 5,
                "description": "Sarcastic"
            },
            {
                "id": 10,
                "description": "Night owl"
            }
        ],
        "created_at": "2016-02-26T14:00:00Z",
        "updated_at": "2016-03-16T12:00:00Z"
    }
}

And here is the JSON which breaks my tests (notice the ethnicities array was added):

{
    "is_complete": true,
    "user": {
        "id": 1,
        "name": "Juan Carlos",
        "email": "[email protected]",
        "phone": "541122334455",
        "gender": "male",
        "gender_of_interest": "women",
        "birthday": "1990-02-15T00:00:00Z",
        "height": 170,
        "job_title": "Software Engineer",
        "company": "Whim",
        "school": "UADE",
        "degree": {
            "id": 3,
            "description": "Bachelor's"
        },
        "descriptors": [
            {
                "id": 5,
                "description": "Sarcastic"
            },
            {
                "id": 10,
                "description": "Night owl"
            }
        ],
        "ethnicities": [
            {
                "id": 1,
                "description": "Asian"
            },
            {
                "id": 3,
                "description": "Latino"
            }
        ],
        "created_at": "2016-02-26T14:00:00Z",
        "updated_at": "2016-03-16T12:00:00Z"
    }
}

The error that I get is the following:

[GIN] 2016/05/04 - 15:46:38 | 404 |       2.599µs | 127.0.0.1 |   iJS     /
body expected:  

{
    "is_complete": true,
    "user": {
        "id": 1,
        "name": "Juan Carlos",
        "email": "[email protected]",
        "phone": "541122334455",
        "gender": "male",
        "gender_of_interest": "women",
        "birthday": "1990-02-15T00:00:00Z",
        "height": 170,
        "job_title": "Software Engineer",
        "company": "Whim",
        "school": "UADE",
        "degree": {
            "id": 3,
            "description": "Bachelor's"
        },
        "descriptors": [
            {
                "id": 5,
                "description": "Sarcastic"
            },
            {
                "id": 10,
                "description": "Night owl"
            }
        ],
        "ethnicities": [
            {
                "id": 1,
                "description": "Asian"
            },
            {
                "id": 3,
                "description": "Latino"
            }
        ],
        "created_at": "2016-02-26T14:00:00Z",
        "updated_at": "2016-03-16T12:00:00Z"
    }
}  


actual:  

404 page not found  

--- FAIL: iJS zUxMiIsInR5c 
 users.md:22 - body doesn't match  
--- FAIL: TestUsers (0.00s)
FAIL
coverage: 0.0% of statements

Thanks in advance.
JC

Dynamic request variables?

Hello.

Couldn't find any information about this feature. Is it possible to define dynamic variables (i.e. random generated ones) in a request?

Thanks.

Blueprint compatible? Differences?

Just curious to what extent silk's syntax and conventions are similar and/or compatible with blueprint? I ask, because blueprint is a pretty established standard for documenting web APIs (along w/ Swagger and RAML, all of which are interconvertible) and there's already a lot of nice tooling around it (e.g., test2doc).

If different, is it possible to convert silk-based api docs into blueprints or other api-spec formats?

better body assertions: Specify kind of assertion to make

Proposal: better bodies in Silk

Bodies in Silk could make use of the 'formatting' tag thing where not only the data type is specified, but also the kind of assertions that could take place:

``json(mode=exact)
{
  "name": "Silk",
  "released": 2016
}
``
  • Two back tics are used so as not to confuse renderer
  • This assumes that the text after the opening code block line is never rendered (needs validating)

The types of bodies could be:

  • exact - the body must match verbatim
  • same - the JSON must essentially be equal but fields may be in different orders and formatting (whitespace) is ignored
  • subset - each mentioned field is asserted, additional fields in the body is ignored

The syntax highlight language specified could also drive which ParseBody function is called.

Cons

  • When rendered, you can't see the kind of assertion, but that might not be too much of a problem and could always be explained in the plain text surrounding the block.

Alternatives

Other options to consider:

  • the json language specifier might be enough to indicate the datatype, and we could use flags (when running the file) to indicate what type of assertion to make

how does it handle cookies?

Hey!

Silk looks interesting! How does it handle dependent api requests, and cookie based authentication?

Eg a user logs in, then CRUDs a todo?

GraphQL API

Would be nice if you can test a Graphql api backend using silk. Technically it would be possible but might look ugly most of the time. I was using cucumber for the tests but the idea of converting tests to documentation seems really inspiring.

Markdown file length or assertions limit?

Is there any length limit for the .md file? Or any limitations in the number of asserts?

I am having some test failures due to .md parsing.
invalid request: net/http: invalid method "a@st"
** This is not what the .md files contains.

I can reproduce it by replicating a simple test that pass in the same file for several times. When I try to run exactly the same test for 6-7 times.

Any known issue?

Thanks

exclusion patterns

As a Tester
I want to Exclude variable data (timestamps, id fields etc.)
So that I can validate things that matter

Proposed feature

Designate a pattern that will be used to "erase" values from both the expected data, and the response.

Possible solutions

Use a special character field / operator to indicate the exclusion, in this example the prefix ! designates a type of expression being provided.

Text Patterns
Substitute using sed style replace, mostly for text.
!s: s/ABCDE/XXXXX/g

Remove data matching regex, in this case a valid email address. Any value matching the regex would be replaced with nothing.

!r: (?:[a-z0-9!#$%&'+/=?^{|}~-]+(?:.[a-z0-9!#$%&'_+/=?^_{|}~-]+)|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|[\x01-\x09\x0b\x0c\x0e-\x7f])")@(?:(?:a-z0-9?.)+a-z0-9?|[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|[\x01-\x09\x0b\x0c\x0e-\x7f])+)])

JSON Patterns
Replace a field's contents with a dummy value.
!_id: 123

Exclude field from json, the field "_id" field and value would be removed prior to compare.
!x: _id

Request body isn't sent properly

When I send a request with a body option I can't receive form values on server side.

Request

## POST /api
* Content-type: "application/x-www-form-urlencoded"
{
  id: 1
}
===
* Status: 200

Server

r.ParseForm()
fmt.Printf("%v", r.Form)

yields:
map[{ id: "1"}:[] ]

add explicit parameters support

Parameters are hard to self-encode in the path - support them like this:

## GET /path

* ?q=query // comment about q
* ?another=123 // comment about another
* ?something=1 // something about something

Bad reference URL

http://silktest.org/ isn't a good URL reference for this project.

whois:

Tech Name: Domain Manager
Tech Organization: samirnet -domain names for sale
Tech Street: Flat No. 48 Cunningham Apts Edward Road
Tech City: Bangalore
Tech State/Province: Karnataka
Tech Postal Code: 560052
Tech Country: IL
Tech Phone: +91.802260640
Tech Email: [email protected]

Support for standard md line to split request/response

Support for lines in the way of --- instead of === to split request/response

Original example is

# Forgot your password

## POST /users

Create a new user.


{
    "name": "Test user",
    "username": "testreset",
    "email": "[email protected]",
    "password": "abc123",
    "password_confirm": "abc123"
}

===

Standard MD will be:

# Forgot your password

## POST /users

Create a new user.


{
    "name": "Test user",
    "username": "testreset",
    "email": "[email protected]",
    "password": "abc123",
    "password_confirm": "abc123"
}

---

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.