Before and after hooks - uhop/tape-six GitHub Wiki
tape-six supports hooks that run before and after tests. Hooks set up and tear down resources (databases, file systems, etc.) required by the tests. They are scoped to a test suite and run regardless of whether a test passes or fails.
There are two types of hooks:
- Hooks that run before the first test in a test suite and after the last test in a test suite.
- Hooks that run before each test and after each test.
tape-six uses the following names for hook registration functions:
-
beforeAll— Runs before the first test in a test suite. -
afterAll— Runs after the last test in a test suite. -
beforeEach— Runs before each test. -
afterEach— Runs after each test. -
before— an alias forbeforeAll. -
after— an alias forafterAll.
These functions are available as:
- Top-level standalone functions exported from
tape-six. - Instance methods on test():
test.beforeAll(),test.before(), etc. - Instance methods on Tester objects:
t.beforeAll(),t.before(), etc.
Additionally these names are used as properties for test options (see test()).
When called inside a test body, top-level hook functions automatically delegate to the current tester — beforeEach(fn) inside a test body is equivalent to t.beforeEach(fn). Using t.beforeEach() directly is still preferred because it makes the delegation explicit. The top-level form is convenient when porting tests from Mocha or Jest.
All hooks are strictly scoped. Example of two-level hooks:
import {test, beforeAll, afterAll, beforeEach, afterEach} from 'tape-six';
beforeAll(() => console.log('global beforeAll'));
afterAll(() => console.log('global afterAll'));
beforeEach(() => console.log('global beforeEach'));
afterEach(() => console.log('global afterEach'));
test('top level', async t => {
t.pass();
await t.test('nested #1', async t => {
t.pass();
});
await t.test('nested #2', async t => {
t.pass();
});
});The result:
log: global beforeAll
log: global beforeEach
○ top-level
✓ pass - 0.138ms
○ nested #1
✓ pass - 0.051ms
✓ nested #1 1 0 - 0.35ms
○ nested #2
✓ pass - 0.052ms
✓ nested #2 1 0 - 0.147ms
✓ top-level 3 0 - 1.242ms
log: global afterEach
log: global afterAll
As you can see, global hooks run only for top-level tests, not for nested ones.
Let's define hooks for nested tests at the top-level test.
The preferred way is to use t. methods:
import {test, beforeAll, afterAll, beforeEach, afterEach} from 'tape-six';
beforeAll(() => console.log('global beforeAll'));
afterAll(() => console.log('global afterAll'));
beforeEach(() => console.log('global beforeEach'));
afterEach(() => console.log('global afterEach'));
test('top level', async t => {
t.beforeAll(() => console.log('top-level beforeAll'));
t.afterAll(() => console.log('top-level afterAll'));
t.beforeEach(() => console.log('top-level beforeEach'));
t.afterEach(() => console.log('top-level afterEach'));
t.pass();
await t.test('nested #1', async t => {
t.pass();
});
await t.test('nested #2', async t => {
t.pass();
});
});The result:
log: global beforeAll
log: global beforeEach
○ top level
✓ pass - 0.137ms
log: top-level beforeAll
log: top-level beforeEach
○ nested #1
✓ pass - 0.06ms
✓ nested #1 1 0 - 0.381ms
log: top-level afterEach
log: top-level beforeEach
○ nested #2
✓ pass - 0.042ms
✓ nested #2 1 0 - 0.136ms
log: top-level afterEach
log: top-level afterAll
✓ top level 3 0 - 1.469ms
log: global afterEach
log: global afterAll
Highlights:
- Top-level
beforeAllruns before the first nested test. - Top-level
afterAllruns after the last nested test. - Top-level
beforeEachruns before each nested test. - Top-level
afterEachruns after each nested test. - Like before, no global hooks run for nested tests.
We can nest tests as deep as we like but at every scope we should define hooks if we want to run them.
Since top-level hook functions auto-delegate to the current tester when called inside a test body, the following produces the same result:
import {test, beforeAll, afterAll, beforeEach, afterEach} from 'tape-six';
beforeAll(() => console.log('global beforeAll'));
afterAll(() => console.log('global afterAll'));
beforeEach(() => console.log('global beforeEach'));
afterEach(() => console.log('global afterEach'));
test('top level', async t => {
beforeAll(() => console.log('top-level beforeAll'));
afterAll(() => console.log('top-level afterAll'));
beforeEach(() => console.log('top-level beforeEach'));
afterEach(() => console.log('top-level afterEach'));
t.pass();
await t.test('nested #1', async t => {
t.pass();
});
await t.test('nested #2', async t => {
t.pass();
});
});Using t.beforeAll(), t.afterAll(), etc. is still preferred because it makes the scope explicit. The top-level form is convenient for porting tests from Mocha or Jest where before()/after() are globals.
If we want to reuse hooks we can specify them in test options:
import test from 'tape-six';
const sharedOptions = {
beforeEach: () => console.log('shared beforeEach'),
afterEach: () => console.log('shared afterEach')
};
test('top level', sharedOptions, async t => {
t.pass();
await t.test('nested #1', sharedOptions, async t => {
t.pass();
});
await t.test('nested #2', sharedOptions, async t => {
t.pass();
await t.test('deeper', sharedOptions, async t => {
t.pass();
});
});
});The result:
○ top level
✓ pass - 0.869ms
log: shared beforeEach
○ nested #1
✓ pass - 0.046ms
✓ nested #1 1 0 - 0.351ms
log: shared afterEach
log: shared beforeEach
○ nested #2
✓ pass - 0.057ms
log: shared beforeEach
○ deeper
✓ pass - 0.042ms
✓ deeper 1 0 - 0.137ms
log: shared afterEach
✓ nested #2 2 0 - 0.388ms
log: shared afterEach
✓ top level 4 0 - 2.474ms
Now all nested tests run with shared hooks.
Multiple hooks can be registered with the same function, e.g., it is possible to register multiple beforeEach hooks. All "before" hooks run in the order of their registration, beforeAll hooks run before beforeEach hooks. All "after" hooks run in the reversed order of their registration, afterAll hooks run after afterEach hooks.
import test from 'tape-six';
test('top level', async t => {
t.beforeAll(() => console.log('beforeAll #1'));
t.afterAll(() => console.log('afterAll #1'));
t.beforeEach(() => console.log('beforeEach #1'));
t.afterEach(() => console.log('afterEach #1'));
t.pass();
await t.test('nested #1', async t => {
t.pass();
});
t.beforeAll(() => console.log('beforeAll #2'));
t.afterAll(() => console.log('afterAll #2'));
t.beforeEach(() => console.log('beforeEach #2'));
t.afterEach(() => console.log('afterEach #2'));
await t.test('nested #2', async t => {
t.pass();
});
});The result:
○ top level
✓ pass - 0.863ms
log: beforeAll #1
log: beforeEach #1
○ nested #1
✓ pass - 0.056ms
✓ nested #1 1 0 - 0.348ms
log: afterEach #1
log: beforeAll #2
log: beforeEach #1
log: beforeEach #2
○ nested #2
✓ pass - 0.044ms
✓ nested #2 1 0 - 0.146ms
log: afterEach #2
log: afterEach #1
log: afterAll #2
log: afterAll #1
✓ top level 3 0 - 2.443ms
Highlights:
-
nested #1is affected only bybeforeEach #1andafterEach #1. -
nested #2is affected bybeforeEach #1,beforeEach #2,afterEach #2, andafterEach #1. - Before
nested #2runsbeforeAll #2, because it was just registered. - After
nested #2runsafterAll #2andafterAll #1, because they are both in effect. - All "before" hooks run in the normal order: "#1", "#2".
- All "after" hooks run in the reversed order: "#2", "#1".
If a suite has no tests, it will not run any hooks:
import test from 'tape-six';
test('top level', async t => {
t.beforeAll(() => console.log('beforeAll #1'));
t.afterAll(() => console.log('afterAll #1'));
t.beforeEach(() => console.log('beforeEach #1'));
t.afterEach(() => console.log('afterEach #1'));
t.pass();
});The result:
○ top level
✓ pass - 0.874ms
✓ top level 1 0 - 1.482ms
As you can see no hooks are run when there are no tests.