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 for beforeAll.
  • after — an alias for afterAll.

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.

Examples for nested tests

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 beforeAll runs before the first nested test.
  • Top-level afterAll runs after the last nested test.
  • Top-level beforeEach runs before each nested test.
  • Top-level afterEach runs 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.

Examples of reusing hooks

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.

Examples of hooks order

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 #1 is affected only by beforeEach #1 and afterEach #1.
  • nested #2 is affected by beforeEach #1, beforeEach #2, afterEach #2, and afterEach #1.
  • Before nested #2 runs beforeAll #2, because it was just registered.
  • After nested #2 runs afterAll #2 and afterAll #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".

Examples of no tests

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.

⚠️ **GitHub.com Fallback** ⚠️