From 4eea99b20c8e506e0167a56562ec00c9908f6a5a Mon Sep 17 00:00:00 2001 From: Honza Javorek Date: Tue, 14 Jun 2016 19:39:15 +0200 Subject: [PATCH] Introducing Swagger in Dredd. Also addressing: - a bit of https://github.com/apiaryio/dredd/issues/353 - closes https://github.com/apiaryio/dredd/issues/496 --- CONTRIBUTING.md | 2 +- README.md | 9 +- docs/example.md | 2 +- docs/execution-lifecycle.html | 27 ++ docs/execution-lifecycle.md | 36 -- docs/execution-lifecycle/index.html | 27 ++ docs/hooks.md | 13 +- docs/how-it-works.md | 167 ++++++++ docs/how-to-guides.md | 356 +++++++++++++++++ docs/index.md | 9 +- docs/overview.html | 28 ++ docs/overview.md | 381 ------------------- docs/overview/index.html | 28 ++ docs/quickstart.md | 17 +- docs/usage-cli.md-template | 8 +- mkdocs.yml | 10 +- scripts/print-installation-guidelines.coffee | 1 + src/interactive-config.coffee | 4 +- 18 files changed, 679 insertions(+), 446 deletions(-) create mode 100644 docs/execution-lifecycle.html delete mode 100644 docs/execution-lifecycle.md create mode 100644 docs/execution-lifecycle/index.html create mode 100644 docs/how-it-works.md create mode 100644 docs/how-to-guides.md create mode 100644 docs/overview.html delete mode 100644 docs/overview.md create mode 100644 docs/overview/index.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb3ca2d7d..ee6924245 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,7 @@ Recommended workflow: 4. Make sure [test coverage][] didn't drop. 5. Send a Pull Request. -## Handbook for Contributers and Maintainers +## Handbook for Contributors and Maintainers ### Maintainers diff --git a/README.md b/README.md index 0652d2d07..ebf664cbc 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ ![Dredd - HTTP API Testing Framework](img/dredd.png?v=3&raw=true) > **Dredd is a language-agnostic command-line tool for validating -API description written in [API Blueprint][] format against its backend -implementation.** +API description document against backend implementation of the API.** - [Documentation][] - [Changelog][] @@ -20,6 +19,11 @@ Dredd reads your API description and step by step validates whether your API implementation replies with responses as they are described in the documentation. +## API Description Formats Support + +- [API Blueprint][] +- [Swagger][] **(experimental)** + ### Supported Hooks Languages Dredd supports writing [hooks](https://dredd.readthedocs.io/en/latest/hooks/) @@ -68,6 +72,7 @@ $ npm install -g dredd [API Blueprint]: http://apiblueprint.org/ [API Blueprint tutorial]: https://apiblueprint.org/documentation/tutorial.html [API Blueprint examples]: https://github.com/apiaryio/api-blueprint/tree/master/examples +[Swagger]: http://swagger.io/ [Documentation]: https://dredd.readthedocs.io/en/latest/ [Changelog]: CHANGELOG.md diff --git a/docs/example.md b/docs/example.md index 11a93c3ec..a1b82c9e2 100644 --- a/docs/example.md +++ b/docs/example.md @@ -2,7 +2,7 @@ This is an example how to create a simple [Express.js][] API backend application tested with Dredd -## Create API Description File +## Create an API Description Document Create a new API description file, for example in the [API Blueprint][] format. Save it as `api-description.apib` diff --git a/docs/execution-lifecycle.html b/docs/execution-lifecycle.html new file mode 100644 index 000000000..cde5b38de --- /dev/null +++ b/docs/execution-lifecycle.html @@ -0,0 +1,27 @@ + + + + + + + + Page Redirection + + +

+ The page for Dredd usage no longer exists. Please follow + the following link: +

+ + + diff --git a/docs/execution-lifecycle.md b/docs/execution-lifecycle.md deleted file mode 100644 index 37b1f2095..000000000 --- a/docs/execution-lifecycle.md +++ /dev/null @@ -1,36 +0,0 @@ -# Execution Lifecycle - -This section provides the order in which Dredd executes to give a better understanding of how Dredd works. - -1. Load and parse API description documents - - Report parsing warnings -2. Pre-run API description check - - Missing example values for URI template parameters - - Required parameters present in URI - - Report non-parseable JSON bodies - - Report invalid URI parameters - - Report Invalid URI templates -3. Compile HTTP transactions from API description documents - - Inherit headers - - Inherit parameters - - Expand URI templates with parameters -4. Load hooks -5. Test run - - Report test run `start` - - Run `beforeAll` hooks - - For each compiled transaction: - - Report `test start` - - Run `beforeEach` hook - - Run `before` hook - - Send HTTP request - - Receive HTTP response - - Run `beforeEachValidation` hook - - Run `beforeValidation` hook - - Perform [Gavel][] validation - - Run `after` hook - - Run `afterEach` hook - - Report `test end` with result for in-progress reporting - - Run `afterAll` hooks -6. Report test run `end` with result statistics - -[Gavel]: http://blog.apiary.io/2013/07/24/Bam-this-is-Gavel/ diff --git a/docs/execution-lifecycle/index.html b/docs/execution-lifecycle/index.html new file mode 100644 index 000000000..ac8cfc561 --- /dev/null +++ b/docs/execution-lifecycle/index.html @@ -0,0 +1,27 @@ + + + + + + + + Page Redirection + + +

+ The page for Dredd usage no longer exists. Please follow + the following link: +

+ + + diff --git a/docs/hooks.md b/docs/hooks.md index 53b6fa935..6206c1767 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -19,13 +19,13 @@ Hooks are usually used for: You can interact with your server implementation in following languages: -- [Ruby](hooks-ruby.md) -- [Python](hooks-python.md) -- [Node.js](hooks-nodejs.md) -- [PHP](hooks-php.md) - [Go](hooks-go.md) +- [JavaScript (Sandboxed)](hooks-js-sandbox.md) +- [Node.js](hooks-nodejs.md) - [Perl](hooks-perl.md) -- [Sandboxed JavaScript](hooks-js-sandbox.md) +- [PHP](hooks-php.md) +- [Python](hooks-python.md) +- [Ruby](hooks-ruby.md) Dredd doesn't speak your language? [**It's very easy to write support for your language.**](hooks-new-language.md) Your contribution is more than welcome! @@ -73,7 +73,8 @@ $ dredd single_get.md http://machines.apiary.io --names info: Machines > Machines collection > Get Machines ``` -The `Machines > Machines collection > Get Machines` is the name of a transaction which you can use in your hooks. +The `Machines > Machines collection > Get Machines` is the name of a transaction which you can use in your hooks. The same approach works also for Swagger documents. + See [Hooks JavaScript API Reference](#hooks-javascript-api-reference) for broader information of how is it used. ## Types of Hooks diff --git a/docs/how-it-works.md b/docs/how-it-works.md new file mode 100644 index 000000000..c959db0a9 --- /dev/null +++ b/docs/how-it-works.md @@ -0,0 +1,167 @@ +# How It Works + +In a nutshell, Dredd does following: + +1. Takes your API description document, +2. creates expectations based on requests and responses documented in the document, +3. makes requests to tested API, +4. checks whether API responses match the documented responses, +5. reports the results. + +## Versioning + +Dredd follows [Semantic Versioning][]. To ensure certain stability of your Dredd installation (e.g. in CI), pin the version accordingly. You can also use release tags: + +- `npm install dredd` - Installs the latest published version including experimental pre-release versions. +- `npm install dredd@stable` - Skips experimental pre-release versions. Recommended for CI installations. + +If the `User-Agent` header isn't overridden in the API description document, Dredd uses it for sending information about its version number along with every HTTP request it does. + +## Execution Life Cycle + +Following execution life cycle documentation should help you to understand how Dredd works internally and which action goes after which. + +1. Load and parse API description documents + - Report parse errors and warnings +2. Pre-run API description check + - Missing example values for URI template parameters + - Required parameters present in URI + - Report non-parseable JSON bodies + - Report invalid URI parameters + - Report invalid URI templates +3. Compile HTTP transactions from API description documents + - Inherit headers + - Inherit parameters + - Expand URI templates with parameters +4. Load [hooks](hooks.md) +5. Test run + - Report test run `start` + - Run `beforeAll` hooks + - For each compiled transaction: + - Report `test start` + - Run `beforeEach` hook + - Run `before` hook + - Send HTTP request + - Receive HTTP response + - Run `beforeEachValidation` hook + - Run `beforeValidation` hook + - [Perform validation](#automatic-expectations) + - Run `after` hook + - Run `afterEach` hook + - Report `test end` with result for in-progress reporting + - Run `afterAll` hooks +6. Report test run `end` with result statistics + +## Limitations of API Description Formats + +## Automatic Expectations + +Dredd automatically generates expectations on HTTP responses based on examples in the API description with use of [Gavel.js][] library. Please refer to [Gavel][] rules if you want know more. + +### Response Headers Expectations + +- All headers specified in the API description must be present in the response. +- Names of headers are validated in case-insensitive way. +- Only values of headers significant for content negotiation are validated. +- All other headers values can differ. + +When using [Swagger][], headers are taken from [`response.headers`](http://swagger.io/specification/#responseHeaders). If document specifies `produces`, it is propagated as response's `Content-Type` header. + +### Response Body Expectations + +By default Dredd validates HTTP response body against [JSON Schema][] inferred from the API description under test. The effective JSON Schema is taken from following places (the order goes from the highest priority to the lowest): + +#### API Blueprint + +1. `+ Schema` section - provided custom JSON Schema (draft v4 or v3) will be used. +2. `+ Attributes` section with data structure description in [MSON][] - API Blueprint parser automatically generates JSON Schema from MSON. +3. `+ Body` section with sample JSON payload - [Gavel.js][], which is responsible for validation in Dredd, automatically infers some basic expectations described below. + +This order [exactly follows the API Blueprint specification](https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md#relation-of-body-schema-and-attributes-sections). + +#### Swagger + +1. [`response.schema`](http://swagger.io/specification/#responseSchema) - provided JSON Schema will be used. +2. [`response.examples`](http://swagger.io/specification/#responseExamples) with sample JSON payload - [Gavel.js][], which is responsible for validation in Dredd, automatically infers some basic expectations described below. + +#### Gavel's Expectations + +- All JSON keys on any level given in the sample must be present in the response's JSON. +- Response's JSON values must be of the same JSON primitive type. +- All JSON values can differ. +- Arrays can have additional items, type or structure of the items is not validated. +- Plain text must match perfectly. + +### Custom Expectations + +You can make your own custom expectations in [hooks](hooks.md). For instance, check out how to employ [Chai.js assertions](hooks.md#using-chai-assertions). + +## Making Your API Description Ready for Testing + +It's very likely that your API description document will not be testable __as is__. This section should help you to learn how to solve the most common issues. + +### URI Parameters + +Both [API Blueprint][] and [Swagger][] allow usage of URI templates. In order to have an API description which is testable, you need to describe all parameters used in URI (path or query) and provide sample values to make Dredd able to expand URI templates with given sample values. + +TODO!!! ... + +### Request Headers + +TODO!!! ... + +#### Swagger + +When using [Swagger][], there is no way how to make Dredd send specific HTTP headers except of those significant for content negotiation. This is a known limitation of the Swagger format. + +> **Note:** Please mind that Swagger's `"in": "header"` parameter doesn't provide any way to specify HTTP header name. The meaning of the `name` property is _name of the parameter_, not _name of the header_. + +- `consumes` is propagated as request's `Content-Type` header. +- `produces` is propagated as request's `Accept` header. +- Response's `Content-Type` header overrides any `consumes`. + +### Request Body + +TODO!!! ... + +#### Swagger + +Request body is inferred from [operation's `"in": "body"` and `"in": "form"` parameters](http://swagger.io/specification/#parameterObject). Since Swagger doesn't provide any way to specify raw JSON sample for the request body, Dredd's [Swagger Adapter][] generates some sample values from JSON Schema provided in the [`schema`](http://swagger.io/specification/#parameterSchema) property. Following rules apply when the adapter fills values of the properties, ordered by precedence: + +1. Value of `default`. +2. First value from `enum`. +3. Dummy, generated value. + +### How Dredd Works With HTTP Transactions + +#### Multiple Requests and Responses (API Blueprint) + +TODO!!! ... + +#### Default Response (Swagger) + +TODO!!! ... + +#### Responses with Status Code Other than 2xx (Swagger) + +TODO!!! ... + +#### Supported Media Types in `produces` and `consumes` (Swagger) + +TODO!!! ... + +#### Limitations of the Swagger Format + +TODO!!! ... +- Responses with the same Content Type & status code but different examples. +- Requests with the same Content Type but different examples. + + +[Semantic Versioning]: http://semver.org/ +[API Blueprint]: http://apiblueprint.org/ +[Swagger]: http://swagger.io/ +[Gavel.js]: https://github.com/apiaryio/gavel.js +[Gavel]: https://www.relishapp.com/apiary/gavel/docs +[MSON]: https://github.com/apiaryio/mson +[JSON Schema]: http://json-schema.org/ +[Swagger Adapter]: https://github.com/apiaryio/fury-adapter-swagger/ diff --git a/docs/how-to-guides.md b/docs/how-to-guides.md new file mode 100644 index 000000000..42bd16d5f --- /dev/null +++ b/docs/how-to-guides.md @@ -0,0 +1,356 @@ +# How-To Guides + +In the following guides you can find tips and best practices how to cope with some common tasks. While searching this page for particular keywords can give you quick results, reading the whole section should help you to learn some of the Dredd's core concepts and usual ways how to approach problems when testing with Dredd. + +## Isolation of HTTP Transactions + +Requests in the API description usually aren't sorted in order to comply with logical workflow of the tested application. To get the best results from testing with Dredd, you should ensure each resource action ([API Blueprint][]) or operation ([Swagger][]) is executed in isolated context. This can be easily achieved using [hooks](hooks.md), where you can provide your own setup and teardown code for each HTTP transaction. + +You should understand that testing with Dredd is an analogy to **unit tests** of your application code. In unit tests, each unit should be testable without any dependency on other units or previous tests. + +### API Blueprint Example + +Common case is to solve a situation where we want to test deleting of a resource. Obviously, to test deleting of a resource, we first need to create one. However, the order of HTTP transactions can be pretty much random in the API description: + +```markdown +FORMAT: 1A + +# Categories API + +## Categories [/categories] + +### Create a Category [POST] ++ Response 201 + +## Category [/category/{id}] ++ Parameters + + id: 42 (required) + +### Delete a Category [DELETE] ++ Response 204 + +## Category Items [/category/{id}/items] ++ Parameters + + id: 42 (required) + +## Create an Item [POST] ++ Response 201 +``` + +To solve this situation, it's recommended to isolate the deletion test by [hooks](hooks.md). Providing `before` hook, we can ensure the database fixture will be present every time Dredd will try to send the request to delete a category item. + +To have an idea where we can hook our arbitrary code, we should first ask Dredd to list all available transaction names: + +``` +$ dredd api-description.apib http://localhost:3000 --names +info: Categories > Create a category +info: Category > Delete a category +info: Category Items > Create an item +``` + +Now we can create a `hooks.js` file. The file will contain setup and teardown of the database fixture: + +```javascript +hooks = require('hooks'); +db = require('./src/db'); + +beforeAll(function() { + db.cleanUp(); +}); + +afterEach(function(transaction) { + db.cleanUp(); +}); + +before('Category > Delete a Category', function() { + db.createCategory({id: 42}); +}); + +before('Category Items > Create an Item', function() { + db.createCategory({id: 42}); +}); +``` + +## Testing API Workflows + +Often you want to test a sequence of steps, a scenario, rather than just one request-response pair in isolation. Since the API description formats are quite limited in their support of documenting scenarios, Dredd probably isn't the best tool to provide you with this kind of testing. There are some tricks though, which can help you to work around some of the limitations. + +> **Note:** [API Blueprint][] prepares direct support for testing and scenarios. Interested? + Check out [apiaryio/api-blueprint#21](https://github.com/apiaryio/api-blueprint/issues/21)! + +To test various scenarios, you will want to write each of them into a separate API description document. To load them during a single test run, use the [--path](usage-cli.md#-path-p) option. + +For workflows to work properly, you'll also need to keep **shared context** between individual HTTP transactions. You can use [hooks](hooks.md) in order to achieve that. See tips on how to [pass data between transactions](hooks.md#sharing-data-between-steps-in-request-stash). + +### API Blueprint Example + +Imagine we have a simple workflow described: + +```markdown +FORMAT: 1A + +# My Scenario + +## POST /login + ++ Request (application/json) + + {"username": "john", "password": "d0e"} + + ++ Response 200 (application/json) + + {"token": "s3cr3t"} + +## GET /cars + ++ Response 200 (application/json) + + [ + {"id": "42", "color": "red"} + ] + +## PATCH /cars/{id} ++ Parameters + + id: 1 (string, required) + ++ Request (application/json) + + {"color": "yellow"} + ++ Response 200 (application/json) + + [ + {"id": 42, "color": "yellow"} + ] + +``` + +To have an idea where we can hook our arbitrary code, we should first ask Dredd to list all available transaction names: + +``` +$ dredd api-description.apib http://localhost:3000 --names +info: /login > POST +info: /cars > GET +info: /cars/{id} > PATCH +``` + +Now we can create a `hooks.js` file. The code of the file will use global `stash` variable to share data between requests: + +```javascript +hooks = require('hooks'); +db = require('./src/db'); + +stash = {} + +// Stash the token we've got +after('/login > POST', function (transaction) { + stash.token = JSON.parse(transaction.real.body).token; +}); + +// Add the token to all HTTP transactions +beforeEach(function (transaction) { + if (stash.token) { + transaction.headers['X-Api-Key'] = stash.token + }; +}); + +// Stash the car ID we've got +after('/cars > GET', function (transaction) { + stash.carId = JSON.parse(transaction.real.body).id; +}); + +// Replace car ID in request with the one we've stashed +before('/cars/{id} > PATCH', function (transaction) { + transaction.fullPath = transaction.fullPath.replace('42', stash.carId) + transaction.request.uri = transaction.fullPath +}) +``` + +## Integrating Dredd with Your Test Suite + +Generally, if you want to add Dredd to your existing test suite, you can just save Dredd configuration in the `dredd.yml` file and add call for `dredd` command to your task runner. + +There are also some packages which make the integration a piece of cake: + +- [grunt-dredd](https://github.com/mfgea/grunt-dredd) +- [dredd-rack](https://github.com/gonzalo-bulnes/dredd-rack) +- [meteor-dredd](https://github.com/storeness/meteor-dredd) + +To find more, search for `dredd` in your favorite language's package index. + +## Continuous Integration + +It's a good practice to make Dredd part of your continuous integration workflow. Only that way you can ensure that application code you'll produce won't break the contract you provide in your API documentation. + +Dredd's interactive configuration wizard, `dredd init`, can help you with setting up `dredd.yml` configuration file and with modifying or generating CI configuration files for [Travis CI][] or [CircleCI][]. + +If you prefer to add Dredd manually or you look for inspiration on how to add Dredd to other continuous integration services, see examples below: + +### `circle.yml` Configuration File for [CircleCI][] + +``` +dependencies: + pre: + - npm install -g dredd@stable +test: + pre: + - dredd apiary.apib http://localhost:3000 +``` + +### `.travis.yml` Configuration File for [Travis CI][] + +``` +before_install: + - npm install -g dredd@stable +before_script: + - dredd apiary.apib http://localhost:3000 +``` + +## Authenticated APIs + +Dredd supports all common authentication schemes: + +- Basic access authentication +- Digest access authentication +- OAuth (any version) +- CSRF tokens +- ... + +Use `user` setting in your configuration file or `--user` argument to provide HTTP basic authentication: + +``` +--user=user:password +``` + +Most of the authentication schemes use HTTP header for carrying the authentication data. If you don't want to add authentication HTTP header to every request in the API description, you can instruct Dredd to do it for you: + +``` +--headers="Authorization: Basic YmVuOnBhc3M=" +``` + +## Sending Multipart Requests + +API Blueprint format supports `multipart/form-data` media type and so does Dredd. In the example below, Dredd will automatically add `LF` to all lines in request body: + +```markdown +# POST /images + ++ Request (multipart/form-data;boundary=---BOUNDARY) + + Headers + + Authorization: qwertyqwerty + + + Body + + ---BOUNDARY + Content-Disposition: form-data; name="json" + + + {"name": "test"} + ---BOUNDARY + Content-Disposition: form-data; name="image"; filename="filename.jpg" + Content-Type: image/jpeg + + data + ---BOUNDARY-- + +``` + +## Multiple Requests and Responses within One API Blueprint Action + +> **Note:** Currently support for multiple requests and responses is still under development. Refer to issues [#25](https://github.com/apiaryio/dredd/issues/25) and [#78](https://github.com/apiaryio/dredd/issues/78) for details. Support for URI parameters specific to a single request within one action is also limited. Solving [#227](https://github.com/apiaryio/dredd/issues/227) should unblock many related problems. Following guide is pretty much a workaround around all this. + +To test multiple requests and responses within one action in Dredd, you need to cluster them into pairs: + +```markdown +FORMAT: 1A + +# My API + +## Resource [/resource/{id}] + ++ Parameters + + id: 42 (required) + +### Update Resource [PATCH] + ++ Request (application/json) + + {"color": "yellow"} + + ++ Response 200 (application/json) + + {"color": "yellow", "id": 1} + + ++ Request Edge Case (application/json) + + {"weight": 1} + ++ Response 400 (application/vnd.error+json) + + {"message": "Validation failed"} + +``` + +Dredd will detect two HTTP transaction examples and will compile following transaction names: + +``` +$ dredd api-description.apib http://localhost --names +info: Beginning Dredd testing... +info: Resource > Update Resource > Example 1 +info: Resource > Update Resource > Example 2 +``` + +In case you need to perform particular request with different URI parameters and standard inheritance of URI parameters isn't working for you, try [modifying transaction before its execution](hooks.md#modifying-transactions-prior-to-execution) in hooks. + +## Using Apiary Reporter and Apiary Tests + +Command-line output of complex HTTP responses and expectations can be hard to read. To tackle the problem, you can use Dredd to send test reports to [Apiary][]. Apiary provides a comfortable interface for browsing complex test reports: + +``` +$ dredd apiary.apib http://localhost --reporter=apiary +info: Using apiary reporter. +warn: Apiary reporter environment variable APIARY_API_KEY or APIARY_API_NAME not defined. +info: Beginning Dredd testing... +pass: DELETE /honey duration: 884ms +complete: 1 passing, 0 failing, 0 errors, 0 skipped, 1 total +complete: Tests took 1631ms +complete: See results in Apiary at: https://app.apiary.io/public/tests/run/74d20a82-55c5-49bb-aac9-a3a5a7450f06 +``` + +![Apiary Tests](https://raw.github.com/apiaryio/dredd/master/img/apiary-tests.png?raw=true) + +### Saving Test Reports under Your Account in Apiary + +As you can see on the screenshot, the test reports are anonymous by default and will expire after some time. However, if you provide Apiary credentials, your test reports will appear on the _Tests_ page of your API Project. This is great especially for introspection of test reports from Continuous Integration. + +To get and setup credentials, just follow the tutorial in Apiary: + +![Apiary Tests Tutorial](https://raw.github.com/apiaryio/dredd/master/img/apiary-tests-tutorial.png?raw=true) + +As you can see, the parameters go like this: + +``` +$ dredd -c apiaryApiKey: -c apiaryApiName: +``` + +In addition to using parameters and `dredd.yml`, you can also use environment variables: + +- `APIARY_API_KEY=` - Alternative way to pass credentials to Apiary Reporter. +- `APIARY_API_NAME=` - Alternative way to pass credentials to Apiary Reporter. + +When sending test reports to Apiary, Dredd inspects the environment where it was executed and sends some information about it alongside test results. Those are used mainly for detection whether the environment is Continuous Integration and also, they help you to identify individual test reports on the _Tests_ page. You can use the following variables to tell Dredd what to send: + +- agent (string) - `DREDD_AGENT` or current user in the OS +- hostname (string) - `DREDD_HOSTNAME` or hostname of the OS +- CI (boolean) - looks for `TRAVIS`, `CIRCLE`, `CI`, `DRONE`, `BUILD_ID`, ... + + +[Apiary]: https://apiary.io/ +[API Blueprint]: http://apiblueprint.org/ +[Swagger]: http://swagger.io/ +[Travis CI]: https://travis-ci.org/ +[CircleCI]: https://circleci.com/ diff --git a/docs/index.md b/docs/index.md index f5fd27c65..c4a4b8f9b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,13 +9,17 @@ ![Dredd - HTTP API Testing Framework](https://raw.github.com/apiaryio/dredd/master/img/dredd.png?v=3&raw=true) > **Dredd is a language-agnostic command-line tool for validating -API description written in [API Blueprint][] format against its backend -implementation.** +API description document against backend implementation of the API.** Dredd reads your API description and step by step validates whether your API implementation replies with responses as they are described in the documentation. +## API Description Formats Support + +- [API Blueprint][] +- [Swagger][] **(experimental)** + ### Supported Hooks Languages Dredd supports writing [hooks](https://dredd.readthedocs.io/en/latest/hooks/) @@ -74,6 +78,7 @@ Dredd supports writing [hooks](https://dredd.readthedocs.io/en/latest/hooks/) [API Blueprint]: http://apiblueprint.org/ +[Swagger]: http://swagger.io/ [GitHub Repository]: https://github.com/apiaryio/dredd [Bug Tracker]: https://github.com/apiaryio/dredd/issues?q=is%3Aopen diff --git a/docs/overview.html b/docs/overview.html new file mode 100644 index 000000000..e33ddba6d --- /dev/null +++ b/docs/overview.html @@ -0,0 +1,28 @@ + + + + + + + + Page Redirection + + +

+ The page for Dredd usage no longer exists. Please follow one + of the following links: +

+ + + diff --git a/docs/overview.md b/docs/overview.md deleted file mode 100644 index c320c3438..000000000 --- a/docs/overview.md +++ /dev/null @@ -1,381 +0,0 @@ -# Dredd Overview - -Dredd works by taking your API description document, creating expectations based on the requests and responses in the description, making requests to your API, and seeing if the responses match. Dredd automatically builds these expectations from the API description every time the tests are run. - -## Versioning - -Dredd follows [Semantic Versioning][]. To ensure certain stability of your Dredd installation (e.g. in CI), pin the version accordingly. You can also use release tags: - -- `npm install dredd` - Installs the latest published version including experimental pre-release versions. -- `npm install dredd@stable` - Skips experimental pre-release versions. Recommended for CI installations. - -## Automatic Expectations - -Dredd automatically generates expectations on HTTP responses based on examples in the API description with use of [Gavel.js](https://github.com/apiaryio/gavel.js) library. Please refer to [Gavel](https://www.relishapp.com/apiary/gavel/docs) rules if you want know more. - -**Remember:** You can easily write additional [custom expectations](hooks.md#using-chai-assertions) in hooks. - -### Headers Expectations - -- All headers given in example must be present in the response -- Only values of headers significant for content negotiation are validated -- All other headers values can differ. - -### Body Expectations - -- All JSON keys on any level given in the example must be present in the response JSON -- Response JSON values must be of the same JSON primitive type -- All JSON values can differ -- Arrays can have additional items, type or structure is not validated. -- Plain text must match perfectly -- If JSON Schema v4 or JSON Schema v3 is provided in the API description, JSON response must be valid against this schema and JSON example is ignored. - -## Using Apiary Reporter and Apiary Tests - -Command-line output of complex HTTP responses and expectations can be hard to read. To tackle the problem, you can use Dredd to send test reports to [Apiary](https://apiary.io/). Apiary provides a comfortable interface for browsing complex test reports: - -``` -$ dredd apiary.apib http://localhost --reporter=apiary -info: Using apiary reporter. -warn: Apiary reporter environment variable APIARY_API_KEY or APIARY_API_NAME not defined. -info: Beginning Dredd testing... -pass: DELETE /honey duration: 884ms -complete: 1 passing, 0 failing, 0 errors, 0 skipped, 1 total -complete: Tests took 1631ms -complete: See results in Apiary at: https://app.apiary.io/public/tests/run/74d20a82-55c5-49bb-aac9-a3a5a7450f06 -``` - -![Apiary Tests](https://raw.github.com/apiaryio/dredd/master/img/apiary-tests.png?raw=true) - -### Saving Test Reports under Your Account in Apiary - -As you can see on the screenshot, the test reports are anonymous by default and will expire after some time. However, if you provide Apiary credentials, your test reports will appear on the _Tests_ page of your API Project. This is great especially for introspection of test reports from Continuous Integration. - -To get and setup credentials, just follow the tutorial in Apiary: - -![Apiary Tests Tutorial](https://raw.github.com/apiaryio/dredd/master/img/apiary-tests-tutorial.png?raw=true) - -As you can see, the parameters go like this: - -``` -$ dredd -c apiaryApiKey: -c apiaryApiName: -``` - -In addition to using parameters and `dredd.yml`, you can also use environment variables: - -- `APIARY_API_KEY=` - Alternative way to pass credentials to Apiary Reporter. -- `APIARY_API_NAME=` - Alternative way to pass credentials to Apiary Reporter. - -When sending test reports to Apiary, Dredd inspects the environment where it was executed and sends some information about it alongside test results. Those are used mainly for detection whether the environment is Continuous Integration and also, they help you to identify individual test reports on the _Tests_ page. You can use the following variables to tell Dredd what to send: - -- agent (string) - `DREDD_AGENT` or current user in the OS -- hostname (string) - `DREDD_HOSTNAME` or hostname of the OS -- CI (boolean) - looks for `TRAVIS`, `CIRCLE`, `CI`, `DRONE`, `BUILD_ID`, ... - -## Testing API description - -### Documentation Testability - -API Blueprint allows usage of URI templates. If you want to have API description to be complete and testable, do not forget to describe all URI-used parameters and provide examples to make Dredd able to expand URI templates with given example values. - -### Isolation - -API Blueprint structure conforms to the REST paradigm, so in the API Blueprint are documented Resources and their Actions. - -It's very likely that your API Blueprint document will not be testable as-is, because actions in the reference will not be sorted in proper order for API's application logic workflow. - -Proper testing of API description with Dredd is all about isolating each resource action with hook scripts executing code before and after each HTTP transaction to do proper fixtures setup and teardown. - -It's an analogy to **unit testing** of your code. In unit testing, each unit should be testable without any dependency on other units or previous tests. - -> Each API action should be ran in its **isolated context**. - -### Example - -It's usual that you discuss an action in the API description for some entity deletion before an action for re-using this deleted entity. For example in the API Blueprint format: - -```markdown -FORMAT: 1A - -# Categories API - -## Categories [/categories] - -### Create a category [POST] -+ Response 201 - -## Category [/category/{id}] -+ Parameters - + id (required, `42`) - -### Delete a category [DELETE] -+ Response 204 - -## Category Items [/category/{id}/items] -+ Parameters - + id (required, `42`) - -## Create an item [POST] -+ Response 201 - -``` - -In this case, you will have to write a `before` hook for adding a database fixture creating a category executed before HTTP call to action creating item in this category. - -First, retrieve the transaction names. - -``` -dredd api-description.apib http://localhost:3000 --names -info: Categories > Create a category -info: Category > Delete a category -info: Category Items > Create an item -``` - -Now, create a `dreddhooks.js` with a pseudo `db` adapter. - -```javascript -db = require('db'); -hooks = require('hooks'); -beforeAll(function() { - db.cleanUp(); -}); - -afterEach(function(transaction) { - db.cleanUp(); -}); - -before('Category > Delete a category', function() { - db.createCategory({id:42}); -}); - -before('Category Items > Create an item', function() { - db.createCategory({id:42}); -}); -``` - -## Testing API Workflows - -If you want to test some sequence of HTTP steps (workflow or scenario) in your API apart of your API reference or a non RESTful API HTTP workflow, you can run Dredd with multiple API description documents by adding `--path` argument - -Unlike API reference testing, scenarios or workflows steps are in **shared context**, so you may want to [pass data between transactions](hooks.md#sharing-data-between-steps-in-request-stash). - -### Example - -Having following workflow in API description: - -```markdown -FORMAT: 1A - -# My scenario - -## POST /login - -+ Request (application/json) - - { "username": "john", "password":"d0e" } - - -+ Response 200 (application/json) - - { "token": "s3cr3t" } - -## GET /cars - -+ Response 200 (application/json) - - [ { "id": "42", "color": "red"} ] - -## PATCH /cars/{id} -+ Parameters - + id (string,required, `1`) - -+ Request - - { color": "yellow" } - -+ Response 200 - - [ { "id": 42, "color": "yellow" } ] - -``` - -Retrieve transactions names with: - -``` -$ dredd api-description.apib http://localhost:3000 --names -info: /login > POST -info: /cars > GET -info: /cars/{id} > PATCH -``` - -Create a `dredhooks.js` file: - -```javascript -db = require('db'); -hooks = require('hooks'); -stash = {} - -// stash a retrieved token -after('/login > POST', function (transaction) { - stash['token'] = JSON.parse(transaction.real.body)['token']; -}); - -//add token to all HTTP transactions -beforeEach(function (transaction) { - if(stash['token'] != undefined){ - transaction['headers']['X-Api-Key'] = stash['token'] - }; -}); - -//stash returned car ID -after('/cars > GET', function (transaction) { - stash['carId'] = JSON.parse(transaction.real.body)['id']; -}); - -//replace car ID in request for Car resource modification -before('/cars/{id} > PATCH', function (transaction) { - transaction.fullPath = transaction.fullPath.replace('42', stash['carId']) - transaction.request.uri = transaction.fullPath -}) -``` - -## Adding to Existing Test Suite - -Generally, if you want to add Dredd to your existing test suite, you can just save Dredd configuration to the `dredd.yml` and add call for `dredd` command to your task runner. - -In some eco-systems, there is native support for adding Dredd to - -- [grunt-dredd](https://github.com/mfgea/grunt-dredd) a grunt task wrapper -- [dredd-rack](https://github.com/gonzalo-bulnes/dredd-rack) a rake task and rack wrapper -- [meteor-dredd](https://github.com/storeness/meteor-dredd) Dredd Integration for Velocity/Meteor - -## Continuous Integration - -If you didn't make Dredd part to your testing suite which is run in Continuous integration. You can run `dredd init` which will generate a `dredd.yml` configuration file and modify or generate CI configuration. - -If you want to add Dredd to your build manually without use of `dredd.yml` configuration, just add following configuration to your build. - -Example `circle.yml` configuration file for CircleCI: - -``` -dependencies: - pre: - - npm install -g dredd -test: - pre: - - dredd bluprint.md http://localhost:3000 -``` - -Example `travis.yml` configuration file for [Travis CI][]: - -``` -before_install: - - npm install -g dredd -before_script: - - dredd -``` - -### Authenticated APIs - -Using HTTP basic authentication with a CLI argument - -``` ---user user:password -``` - -If you don't want to add some header directly to the API description, you can add header to all requests from command line: - -``` ---headers "Authorization: Basic YmVuOnBhc3M=" -``` - -Adding URL query parameter to all requests - -Dredd supports all possible authentications of HTTP API like: - - basic - - digest - - OAuth 1.0a - - OAuth 2.0 - - adding CSRF tokens - -## Using Multipart Requests - -In following request, dredd will automatically add `LF` to all lines in request body. - -```markdown -# POST /images - -+ Request (multipart/form-data;boundary=---BOUNDARY) - + Headers - - Authorization: qwertyqwerty - - + Body - - ---BOUNDARY - Content-Disposition: form-data; name="json" - - - {"name":"test"} - ---BOUNDARY - Content-Disposition: form-data; name="image"; filename="filename.jpg" - Content-Type: image/jpeg - - data - ---BOUNDARY-- - -``` - -## Multiple Requests/Responses in One Action - -> Disclaimer: This is a workaround until native support for adding custom URI parameters under example and request section will be available in the API Blueprint. - -```markdown -FORMAT: 1A - -# My API - -## Resource [/resource/{id}] - -+ Parameters - + id (required, `42`) - -### Update resource [PATCH] - -+ Request (application/json) - - { "color": "yellow" } - - -+ Response 200 (application/json) - - { "color": "yellow", "id": 1 } - - -+ Request Edge Case (application) - - { "weight": "1"} - -+ Response 400 (application/vnd.error+json) - - {"message": "Validation failed"} - -``` - - -If you need to use different URI address for each example, you can [modify transaction before its execution](hooks.md#modifying-transactions-prior-to-execution) in hooks. - -Dredd will compile following transaction names: - -``` -$ dredd api-description.apib http://localhost --names -info: Beginning Dredd testing... -info: Resource > Update resource > Example 1 -info: Resource > Update resource > Example 2 -``` - -[Semantic Versioning]: http://semver.org/ -[API Blueprint]: http://apiblueprint.org/ -[Travis CI]: https://travis-ci.org/ -[Gavel.js]: https://github.com/apiaryio/gavel.js -[Gavel]: https://www.relishapp.com/apiary/gavel/docs diff --git a/docs/overview/index.html b/docs/overview/index.html new file mode 100644 index 000000000..4b0c83d66 --- /dev/null +++ b/docs/overview/index.html @@ -0,0 +1,28 @@ + + + + + + + + Page Redirection + + +

+ The page for Dredd usage no longer exists. Please follow one + of the following links: +

+ + + diff --git a/docs/quickstart.md b/docs/quickstart.md index 540b9595c..7eca26d49 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -1,6 +1,6 @@ ## Quickstart -- If you don't have [Node.js](https://nodejs.org/) installed, you may want to use [NVM](https://github.com/creationix/nvm) +- If you don't have [Node.js](https://nodejs.org/) installed, you may want to use [NVM](https://github.com/creationix/nvm). - Create an API description document. In this tutorial, we'll use [API Blueprint](https://apiblueprint.org/) format and the `api-description.apib` filename. ``` @@ -9,11 +9,11 @@ Hello World! ``` -- Install Dredd if you haven't already + +- Install Dredd. ``` $ npm install -g dredd - ``` - Run interactive configuration: @@ -24,16 +24,15 @@ $ dredd init ? Command to start API backend server e.g. (bundle exec rails server) ? URL of tested API endpoint: http://localhost:3000 ? Programming language of hooks: -❯ ruby +❯ nodejs python - nodejs + ruby + ... ? Dredd is best served with Continuous Integration. Create CircleCI config for Dredd? Yes - ``` -- Install [Ruby](hooks-ruby.md) or [Python](hooks-python.md) handler if needed - -- Run dredd +- Install hook handler for your favorite language. See [hooks documentation](hooks.md) for list of supported languages. +- Run the `dredd` command! ``` $ dredd diff --git a/docs/usage-cli.md-template b/docs/usage-cli.md-template index 1206140e0..a2645efbd 100644 --- a/docs/usage-cli.md-template +++ b/docs/usage-cli.md-template @@ -16,8 +16,8 @@ $ dredd ./apiary.md 'http://localhost:3000' ### API Description Document (string) -URL or path to the API description document (e.g. API Blueprint). -**Sample values:** `./apiary.apib`, `http://example.com/apiary.apib` +URL or path to the API description document (API Blueprint, Swagger). +**Sample values:** `./api-blueprint.apib`, `./swagger.yml`, `./swagger.json`, `http://example.com/api-blueprint.apib` ### API Location (string) @@ -66,10 +66,12 @@ level: info timestamp: false silent: false path: [] -blueprint: apiary.apib +blueprint: api-description.apib endpoint: "http://localhost:3000" ``` +> **Note:** Do not get confused by Dredd using a keyword `blueprint` also for paths to Swagger documents. This is for historical reasons and will be changed in the future. + ## CLI Options Reference Remember you can always list all available arguments by `dredd --help`. diff --git a/mkdocs.yml b/mkdocs.yml index 6ef841e25..7777bdb0f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,8 +7,7 @@ pages: - Dredd: - About Dredd: index.md - Quickstart: quickstart.md - - Execution Lifecycle: execution-lifecycle.md - - Overview: overview.md + - How It Works: how-it-works.md - Usage: - Command-line Interface: usage-cli.md - From JavaScript: usage-js.md @@ -23,7 +22,8 @@ pages: - Python: hooks-python.md - Ruby: hooks-ruby.md - Other Languages: hooks-new-language.md - - Example: + - Guides: + - How-To Guides: how-to-guides.md - Full Example: example.md theme: readthedocs @@ -39,3 +39,7 @@ markdown_extensions: extra_templates: - usage.html - usage/index.html + - overview.html + - overview/index.html + - execution-lifecycle.html + - execution-lifecycle/index.html diff --git a/scripts/print-installation-guidelines.coffee b/scripts/print-installation-guidelines.coffee index 09608be41..6e8b9508d 100644 --- a/scripts/print-installation-guidelines.coffee +++ b/scripts/print-installation-guidelines.coffee @@ -9,4 +9,5 @@ console.error(colors.cyan(""" :: prefer stability over new features (e.g. in CI) :: :: :: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + """)) diff --git a/src/interactive-config.coffee b/src/interactive-config.coffee index 0262fe918..85a9c9b3e 100644 --- a/src/interactive-config.coffee +++ b/src/interactive-config.coffee @@ -143,7 +143,7 @@ interactiveConfig.updateCircle = () -> config['dependencies'] ?= {} config['dependencies']['pre'] ?= [] - config['dependencies']['pre'].push("npm install -g dredd")if config['dependencies']['pre'].indexOf("npm install -g dredd") == -1 + config['dependencies']['pre'].push("npm install -g dredd@stable") if config['dependencies']['pre'].indexOf("npm install -g dredd@stable") == -1 config['test'] ?= {} config['test']['pre'] ?= [] @@ -162,7 +162,7 @@ interactiveConfig.updateTravis = () -> config = {} config['before_install'] ?= [] - config['before_install'].push("npm install -g dredd") if config['before_install'].indexOf("npm install -g dredd") == -1 + config['before_install'].push("npm install -g dredd@stable") if config['before_install'].indexOf("npm install -g dredd@stable") == -1 config['before_script'] ?= [] config['before_script'].push("dredd") if config['before_script'].indexOf("dredd") == -1