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.
openapi: "3.0.0"
info:
title: Blog API
version: "1.0"
paths:
/articles:
get:
summary: List articles
description: Retrieve a list of all articles
responses:
"200":
description: Articles list
content:
"application/json; charset=utf-8":
example:
- id: 1
title: Creamy cucumber salad
text: "Slice cucumbers…"
post:
summary: Publish an article
description: Create and publish a new article
requestBody:
content:
"application/json; charset=utf-8":
example:
title: Crispy schnitzel
text: "Prepare eggs…"
responses:
"201":
description: New article
content:
"application/json; charset=utf-8":
example:
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 runs hooks written in JavaScript, which are 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 > 201 > application/json; charset=utf-8',
(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; charset=utf-8. This path-like identifier is a transaction name.
Running Dredd with hooks#
With the API instance running locally at http://127.0.0.1:3000, you can now run Dredd with hooks using the --hookfiles option:
dredd ./blog.yaml http://127.0.0.1:3000 --hookfiles=./hooks.js
Now the tests should pass even if publishing new article requires auth.
JavaScript hooks#
Dredd is written in JavaScript and runs JavaScript hooks out of the box — no extra installation required.
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.yaml http://127.0.0.1:3000 --names
info: /articles > List articles > 200 > application/json; charset=utf-8
skip: GET (200) /articles
info: /articles > Publish an article > 201 > application/json; charset=utf-8
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; charset=utf-8/articles > Publish an article > 201 > application/json; charset=utf-8
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:
beforeAllcalled with all HTTP transactions before the whole test runbeforeEachcalled before each HTTP transactionbeforecalled before a single HTTP transactionbeforeEachValidationcalled before each HTTP transaction is validatedbeforeValidationcalled before a single HTTP transaction is validatedaftercalled after a single HTTP transactionafterEachcalled after each HTTP transactionafterAllcalled with all HTTP transactions after the whole test run