Hooks

Dredd supports hooks, which are blocks of arbitrary code that run before or after each test step. The concept is similar to XUnit’s setUp and tearDown functions, Cucumber hooks, or Git hooks. Hooks are usually used for:

  • Loading database fixtures,
  • cleaning up after test step(s),
  • handling auth and sessions,
  • passing data between transactions (saving state from responses),
  • modifying a request generated from the API description,
  • changing generated expectations,
  • setting custom expectations,
  • debugging by logging stuff.

Getting started

Let’s have a description of a blog API, which allows to list all articles, and to publish a new one.

FORMAT: 1A

# Blog API
## Articles [/articles]
### List articles [GET]

+ Response 200 (application/json)

        [
          {
            "id": 1,
            "title": "Creamy cucumber salad",
            "text": "Slice cucumbers…"
          }
        ]

### Publish an article [POST]

+ Request (application/json)

        {
          "title": "Crispy schnitzel",
          "text": "Prepare eggs…"
        }

+ Response 201 (application/json)

        {
          "id": 2,
          "title": "Crispy schnitzel",
          "text": "Prepare eggs…"
        }
swagger: "2.0"
info:
  title: "Blog API"
  version: "1.0"
consumes:
  - "application/json"
produces:
  - "application/json"
paths:
  "/articles":
    x-summary: "Articles"
    get:
      summary: "List articles"
      description: "Retrieve a list of all articles"
      responses:
        200:
          description: "Articles list"
          examples:
            "application/json":
              - id: 1
                title: "Creamy cucumber salad"
                text: "Slice cucumbers…"
    post:
      summary: "Publish an article"
      description: "Create and publish a new article"
      parameters:
        - name: "body"
          in: "body"
          schema:
            example:
              title: "Crispy schnitzel"
              text: "Prepare eggs…"
      responses:
        201:
          description: "New article"
          examples:
            "application/json":
              id: 2
              title: "Crispy schnitzel"
              text: "Prepare eggs…"

Now let’s say the real instance of the API has the POST request protected so it is not possible for everyone to publish new articles. We do not want to hardcode secret tokens in our API description, but we want to get Dredd to pass the auth. This is where the hooks can help.

Writing hooks

Hooks are functions, which are registered to be ran for a specific test step (HTTP transaction) and at a specific point in Dredd’s execution life cycle. Hook functions take one or more transaction objects, which they can modify. Let’s use hooks to add an Authorization header to Dredd’s request.

Dredd supports writing hooks in multiple programming languages, but we’ll go with JavaScript hooks in this tutorial as they’re available out of the box.

Let’s create a file called hooks.js with the following content:

const hooks = require('hooks');

hooks.before('Articles > Publish an article', (transaction) => {
  transaction.request.headers.Authorization = 'Basic: YWxhZGRpbjpvcGVuc2VzYW1l';
});

As you can see, we’re registering the hook function to be executed before the HTTP transaction Articles > Publish an article. This path-like identifier is a transaction name.

Let’s create a file called hooks.js with the following content:

const hooks = require('hooks');

hooks.before('Articles > Publish an article > 201 > application/json', (transaction) => {
  transaction.request.headers.Authorization = 'Basic: YWxhZGRpbjpvcGVuc2VzYW1l';
});

As you can see, we’re registering the hook function to be executed before the HTTP transaction Articles > Publish an article > 201 > application/json. This path-like identifier is a transaction name.

Running Dredd with hooks

With the API instance running locally at http://127.0.0.1, you can now run Dredd with hooks using the --hookfiles option:

dredd ./blog.apib http://127.0.0.1 --hookfiles=./hooks.js
dredd ./blog.yaml http://127.0.0.1 --hookfiles=./hooks.js

Now the tests should pass even if publishing new article requires auth.

Supported languages

Dredd itself is written in JavaScript, so it supports JavaScript hooks out of the box. Running hooks in other languages requires installing a dedicated hook handler. Supported languages are:

Note

If you don’t see your favorite language, it’s fairly easy to contribute support for it! Join the Contributors Hall of Fame where we praise those who added support for additional languages.

(Especially if your language of choice is Java, there’s an eternal fame and glory waiting for you - see #875)

Transaction names

Transaction names are path-like strings, which allow hook functions to address specific HTTP transactions. They intuitively follow the structure of your API description document.

You can get a list of all transaction names available in your API description document by calling Dredd with the --names option:

$ dredd ./blog.apib http://127.0.0.1 --names
info: Beginning Dredd testing...
info: Articles > List articles
skip: GET (200) /articles
info: Articles > Publish an article
skip: POST (201) /articles
complete: 0 passing, 0 failing, 0 errors, 2 skipped, 2 total
complete: Tests took 9ms

As you can see, the document ./blog.apib contains two transactions, which you can address in hooks as:

  • Articles > List articles
  • Articles > Publish an article
$ dredd ./blog.yaml http://127.0.0.1 --names
info: Beginning Dredd testing...
info: Articles > List articles > 200 > application/json
skip: GET (200) /articles
info: Articles > Publish an article > 201 > application/json
skip: POST (201) /articles
complete: 0 passing, 0 failing, 0 errors, 2 skipped, 2 total
complete: Tests took 9ms

As you can see, the document ./blog.yaml contains two transactions, which you can address in hooks as:

  • Articles > List articles > 200 > application/json
  • Articles > Publish an article > 201 > application/json

Note

The transaction names and the --names workflow mostly do their job, but with many documented flaws. A successor to transaction names is being designed in #227

Types of hooks

Hooks get executed at specific points in Dredd’s execution life cycle. Available types of hooks are:

  • beforeAll called at the beginning of the whole test run
  • beforeEach called before each HTTP transaction
  • before called before a specific HTTP transaction
  • beforeEachValidation called before each HTTP transaction is validated
  • beforeValidation called before a specific HTTP transaction is validated
  • after called after a specific HTTP transaction regardless its result
  • afterEach called after each HTTP transaction
  • afterAll called after whole test run

Hooks inside Docker

As mentioned in Supported languages, running hooks written in languages other than JavaScript requires a dedicated hook handler. Hook handler is a separate process, which communicates with Dredd over a TCP socket.

If you’re running Dredd inside Docker, you may want to use a separate container for the hook handler and then run all your containers together as described in the Docker Compose section.

However, hooks were not originally designed with this scenario in mind. Dredd gets a name of (or path to) the hook handler in --language and then starts it as a child process. To work around this, fool Dredd with a dummy script and set --hooks-worker-handler-host together with --hooks-worker-handler-port to point Dredd’s TCP communication to the other container.

Note

The issue described above is tracked in #755.