Hooks
Testosa provides a hooks interface to perform actions before and after all and/or each endpoint test. Hooks are currently only supported in JavaScript and are defined as a module or class that implements afterAll
, afterEach
, beforeAll
and beforeEach
methods that will be called when any of these events occurs.
Usage
To start using hooks:
Create a JavaScript file with the hook functions you wish to use in your tests. Supported functions include
afterAll
,afterEach
,beforeAll
andbeforeEach
. See the hooks functions reference for details.// path/to/your/hooks-file.js const beforeAll = async () => { /* logic */ }; const beforeEach = async (transaction) => { /* logic */ }; const afterEach = async (transaction) => { /* logic */ }; const afterAll = async () => { /* logic */ }; module.exports = { afterAll, afterEach, beforeAll, beforeEach }
Update your Testosa config to include the
hooksFilePath
option and set it to the relative path of your hooks file.Run Testosa
Methods reference
afterAll
Fn: afterAll() -> [void]
Runs once after all tests have executed regardless of their success/failure status. This function takes no parameters and can be asynchronous if your logic requires it.
Typical use cases:
- Performing global teardown/clean up after your entire test run. For example:
- deprovisioning your test database
- deauthenticating mock users
- deleting mock resources
- etc.
afterEach
Fn: afterEach(transaction) -> [void]
Runs after each individual test has executed regardless of its success/failure state. The afterEach()
function takes a single parameter, transaction
containing details about the test.
Typical use cases:
- Performing teardown/clean up after each test executes. For example:
- deleting mock resources
- deauthenticating mock users
- debugging
- etc.
Parameters:
Name | Type | Description |
---|---|---|
transaction | [object] | Individual test transaction object. See reference for details |
beforeAll
Fn: beforeAll() -> [void]
Runs once before all tests have executed. This function takes no parameters and can be asynchronous if your logic requires it.
Typical use cases:
- Performing global set up before your test run. For example:
- provisioning your test database
- creating and authenticating mock users
- seeding data in your database
- etc.
beforeEach
Fn: beforeEach(transaction) -> [object]
Runs before each individual test has executed. The beforeEach()
function takes a single parameter, transaction
containing details about the test.
Typical use cases:
- Performing set up before each test executes. For example:
- creating mock resources before modifying or delete them in update or delete endpoint tests
- replacing placeholder parameters (query, header, path etc.) placeholders with real values
- skipping a specific test
- updating the generated test request to trigger a negative scenario
- debugging
- etc.
Parameters:
Name | Type | Description |
---|---|---|
transaction | [object] | Individual test transaction object. See reference for details |
Returns:
Optionally modified transaction
object from input parameter. Only properties within transaction.skip
or properties within transaction.actual.request
may be modified; all other values properties are read only. If a return value is omitted, the original transaction
will be assumed.
Methods parameter (transaction
)
The transaction
parameter is available in the afterEach()
and beforeEach()
hook methods and provides access to inspecting the actual and expected HTTP request and response properties generated by Testosa. transaction
also includes metadata for each test run as well as an optional flag that may be used to skip specific tests. The anatomy of the transaction
object is as follows:
Name | Type | Readonly | Description |
---|---|---|---|
{} | object | no | Individual test transaction object. |
{}.actual | object | no | Object identifying the actual properties of the executed request. |
{}.actual.request.body | object | no | Test request body generated from OpenAPI path examples or schema. |
{}.actual.request.headers | object | no | Test request headers generated from OpenAPI path examples or schema. |
{}.actual.request.method | string | no | Test request method derived from OpenAPI schema. |
{}.actual.request.path | string | no | Test request endpoint derived from OpenAPI path. |
{}.actual.response | object | yes | Response from executed test HTTP request. |
{}.actual.response.body | object | yes | Response body from executed test HTTP request. |
{}.actual.response.headers | object | yes | Response headers from executed test HTTP request. |
{}.actual.response.statusCode | integer | yes | Response status code from executed test HTTP request. |
{}.expected | object | yes | Object identifying the expected properties of the executed request. |
{}.expected.method | string | yes | Expected HTTP method. |
{}.expected.requestBodyContentType | string | yes | Expected HTTP request body content type. |
{}.expected.responseBodyContentType | string | yes | Expected HTTP response body content type. |
{}.expected.statusCode | integer | yes | Expected HTTP response status code. |
{}.meta | object | yes | Object containing meta data for the test execution. |
{}.meta.duration | integer | yes | Test duration. Note: Property is only available in the afterEach test hook. |
{}.meta.endedAt | string<datetime> | yes | Timestamp marking the completion of a test. Note: Property is only available in the afterEach test hook. |
{}.meta.result | string | yes | Test result. Note: Property is only available in the afterEach test hook. |
{}.meta.startedAt | string<datetime> | yes | Timestamp marking the beginning of a test. |
{}.operationId | string | yes | Path operationId if specified in the OpenAPI specification for the endpoint. |
{}.skip | boolean | no | Flag to trigger skipping a test. Default: false. |
Examples
The following examples demonstrates the implementation of some of the test hook use cases in a typical test run:
Use case | Hooks |
---|---|
Create test database | beforeAll |
Create and authenticate a mock user | beforeAll |
Skip single test by operationId | beforeEach |
Skip single test by HTTP method and path | beforeEach |
Create mock resource before modifying it | beforeEach |
Delete mock resource after test | afterEach |
Delete test user after all tests complete | afterAll |
Destroy test database after all tests complete | afterAll |
// path/to/your/hooks-file.js
const authenticateUser = require('../test-helpers/authenticate-user');
const createMockUser = require('../test-helpers/create-user');
const deleteMockUser = require('../test-helpers/delete-user');
const createMockVehicle = require('../test-helpers/create-vehicle');
const deleteMockVehicle = require('../test-helpers/delete-vehicle');
const createTestDb = require('../test-helpers/create-test-db');
const destroyTestDb = require('../test-helpers/destroy-test-db');
let accessToken;
const beforeAll = async () => {
// Create mock database before starting all tests
await createTestDb();
// Create and authenticate a mock user to be used for all authenticated requests
const userName = 'mock-user@my-domain.com';
const password = 's3cret!123';
await createMockUser(userName, password);
accessToken = authenticateUser(userName, password);
};
const beforeEach = async (transaction) => {
// Skip test identified by "get-users" operationId with status code 200
if (transaction.operationId === 'get-users' && transaction.expectedStatusCode === 200) {
transaction.skip = true;
}
// Skip all tests under the PUT /users path definition
if (transaction.expected.method === 'PUT' && transaction.expected.path === '/users') {
transaction.skip = true;
}
// Create mock resource before updating it
if (transaction.expected.method === 'PUT' && transaction.expected.path === '/vehicles/{vehicleId}') {
const vehicle = await createMockVehicle(accessToken);
transaction.expected.path = transaction.expected.path.replace('{vehicleId}', vehicle.id)
}
return transaction;
};
const afterEach = async (transaction) => {
// Delete mock vehicle resource
if (transaction.expected.method === 'PUT' && transaction.expected.path === '/vehicles/{vehicleId}') {
await deleteMockVehicle(accessToken);
}
};
const afterAll = async () => {
// Destroy test database after all tests
await destroyTestDb();
// Delete mock user after all tests
await deleteMockUser(accessToken);
};
module.exports = {
afterAll,
afterEach,
beforeAll,
beforeEach
}