Lifecycle Hooks
Run setup and teardown code around suites and tests with before/after hooks.
#Hooks run around suites and tests. Import them from @guchho/test:
import { beforeAll, beforeEach, afterEach, afterAll } from '@guchho/test';
Hooks are registered inside a suite and apply to that suite and its descendants. Use them to create fixtures, reset shared state, and release resources in a predictable order.
Scope
#Hooks apply to the current suite and every nested suite/test beneath it.
import {
describe,
it,
beforeAll,
beforeEach,
afterEach,
afterAll,
} from '@guchho/test';
describe('database', () => {
let db;
beforeAll(async () => {
db = await openDatabase();
});
beforeEach(async () => {
await db.seed();
});
afterEach(async () => {
await db.clear();
});
afterAll(async () => {
await db.close();
});
it('reads rows', async () => {
// db is ready and seeded
});
});
Ordering
#For a given suite:
beforeAllhooks run in registration order.- For each test:
- ancestor
beforeEachhooks run first, then this suite'sbeforeEachhooks, each in registration order; - the test runs;
- this suite's
afterEachhooks run in reverse registration order, then ancestorafterEachhooks.
- ancestor
afterAllhooks run in reverse registration order.
Nested suites run their own beforeAll/afterAll around their items, while
ancestor beforeEach/afterEach still wrap nested tests.
Async hooks
#Hooks may return a promise. The runner awaits them and applies the run timeout (the resolved configuration timeout). A hook that exceeds the timeout fails.
Failures and cleanup
#- A failing
beforeEachfails the test it wraps.afterEachhooks still run. - A failing
beforeAllcauses the suite's tests to be reported as skipped.afterAllhooks still run. afterEachandafterAllrun after test failures.- Hook errors are recorded separately and reported by reporters. A hook error makes the run unsuccessful.
Example with cleanup:
describe('files', () => {
let file;
beforeEach(() => {
file = createTempFile();
});
afterEach(() => {
// runs even when the test fails
removeTempFile(file);
});
it('fails', () => {
throw new Error('boom');
});
});
Best practices
#- Keep hooks small and deterministic; they run for every matching test.
- Create expensive resources in
beforeAlland release them inafterAll. - Reset mutable state in
beforeEachandafterEachso tests stay isolated. - Return or await promises from async hooks.
- Avoid assertions in cleanup hooks unless the assertion is the point of the hook.
Hook errors in reporters
#Reporters receive hook errors through onHookError(error, display). Each error
includes:
| Field | Description |
|---|---|
type |
beforeAll, afterAll, beforeEach, or afterEach. |
suite |
Suite full name, or <root>. |
error |
The thrown value. |