Skip to content

Commit 645c2da

Browse files
committed
test_runner: add before/after/each hooks
1 parent 7ef069e commit 645c2da

9 files changed

+632
-39
lines changed

doc/api/test.md

+195
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,120 @@ same as [`it([name], { skip: true }[, fn])`][it options].
438438
Shorthand for marking a test as `TODO`,
439439
same as [`it([name], { todo: true }[, fn])`][it options].
440440

441+
### `before([, fn][, options])`
442+
443+
<!-- YAML
444+
added: REPLACEME
445+
-->
446+
447+
* `fn` {Function|AsyncFunction} The hook function.
448+
If the hook uses callbacks,
449+
the callback function is passed as the second argument. **Default:** A no-op
450+
function.
451+
* `options` {Object} Configuration options for the hook. The following
452+
properties are supported:
453+
* `signal` {AbortSignal} Allows aborting an in-progress hook
454+
* `timeout` {number} A number of milliseconds the hook will fail after.
455+
If unspecified, subtests inherit this value from their parent.
456+
**Default:** `Infinity`.
457+
458+
This function is used to create a hook running before running a suite.
459+
460+
```js
461+
describe('tests', async () => {
462+
before(() => console.log('about to run some test'));
463+
it('is a subtest', () => {
464+
assert.ok('some relevant assertion here');
465+
});
466+
});
467+
```
468+
469+
### `after([, fn][, options])`
470+
471+
<!-- YAML
472+
added: REPLACEME
473+
-->
474+
475+
* `fn` {Function|AsyncFunction} The hook function.
476+
If the hook uses callbacks,
477+
the callback function is passed as the second argument. **Default:** A no-op
478+
function.
479+
* `options` {Object} Configuration options for the hook. The following
480+
properties are supported:
481+
* `signal` {AbortSignal} Allows aborting an in-progress hook
482+
* `timeout` {number} A number of milliseconds the hook will fail after.
483+
If unspecified, subtests inherit this value from their parent.
484+
**Default:** `Infinity`.
485+
486+
This function is used to create a hook running after running a suite.
487+
488+
```js
489+
describe('tests', async () => {
490+
after(() => console.log('finished running tests'));
491+
it('is a subtest', () => {
492+
assert.ok('some relevant assertion here');
493+
});
494+
});
495+
```
496+
497+
### `beforeEach([, fn][, options])`
498+
499+
<!-- YAML
500+
added: REPLACEME
501+
-->
502+
503+
* `fn` {Function|AsyncFunction} The hook function.
504+
If the hook uses callbacks,
505+
the callback function is passed as the second argument. **Default:** A no-op
506+
function.
507+
* `options` {Object} Configuration options for the hook. The following
508+
properties are supported:
509+
* `signal` {AbortSignal} Allows aborting an in-progress hook
510+
* `timeout` {number} A number of milliseconds the hook will fail after.
511+
If unspecified, subtests inherit this value from their parent.
512+
**Default:** `Infinity`.
513+
514+
This function is used to create a hook running
515+
before each subtest of the current suite.
516+
517+
```js
518+
describe('tests', async () => {
519+
beforeEach(() => t.diagnostics('about to run a test'));
520+
it('is a subtest', () => {
521+
assert.ok('some relevant assertion here');
522+
});
523+
});
524+
```
525+
526+
### `afterEach([, fn][, options])`
527+
528+
<!-- YAML
529+
added: REPLACEME
530+
-->
531+
532+
* `fn` {Function|AsyncFunction} The hook function.
533+
If the hook uses callbacks,
534+
the callback function is passed as the second argument. **Default:** A no-op
535+
function.
536+
* `options` {Object} Configuration options for the hook. The following
537+
properties are supported:
538+
* `signal` {AbortSignal} Allows aborting an in-progress hook
539+
* `timeout` {number} A number of milliseconds the hook will fail after.
540+
If unspecified, subtests inherit this value from their parent.
541+
**Default:** `Infinity`.
542+
543+
This function is used to create a hook running
544+
after each subtest of the current test.
545+
546+
```js
547+
describe('tests', async () => {
548+
afterEach(() => t.diagnostics('about to run a test'));
549+
it('is a subtest', () => {
550+
assert.ok('some relevant assertion here');
551+
});
552+
});
553+
```
554+
441555
## Class: `TestContext`
442556

443557
<!-- YAML
@@ -448,6 +562,70 @@ An instance of `TestContext` is passed to each test function in order to
448562
interact with the test runner. However, the `TestContext` constructor is not
449563
exposed as part of the API.
450564

565+
### `context.beforeEach([, fn][, options])`
566+
567+
<!-- YAML
568+
added: REPLACEME
569+
-->
570+
571+
* `fn` {Function|AsyncFunction} The hook function. The first argument
572+
to this function is a [`TestContext`][] object. If the hook uses callbacks,
573+
the callback function is passed as the second argument. **Default:** A no-op
574+
function.
575+
* `options` {Object} Configuration options for the hook. The following
576+
properties are supported:
577+
* `signal` {AbortSignal} Allows aborting an in-progress hook
578+
* `timeout` {number} A number of milliseconds the hook will fail after.
579+
If unspecified, subtests inherit this value from their parent.
580+
**Default:** `Infinity`.
581+
582+
This function is used to create a hook running
583+
before each subtest of the current test.
584+
585+
```js
586+
test('top level test', async (t) => {
587+
t.beforeEach((t) => t.diagnostics(`about to run ${t.name}`));
588+
await t.test(
589+
'This is a subtest',
590+
(t) => {
591+
assert.ok('some relevant assertion here');
592+
}
593+
);
594+
});
595+
```
596+
597+
### `context.afterEach([, fn][, options])`
598+
599+
<!-- YAML
600+
added: REPLACEME
601+
-->
602+
603+
* `fn` {Function|AsyncFunction} The hook function. The first argument
604+
to this function is a [`TestContext`][] object. If the hook uses callbacks,
605+
the callback function is passed as the second argument. **Default:** A no-op
606+
function.
607+
* `options` {Object} Configuration options for the hook. The following
608+
properties are supported:
609+
* `signal` {AbortSignal} Allows aborting an in-progress hook
610+
* `timeout` {number} A number of milliseconds the hook will fail after.
611+
If unspecified, subtests inherit this value from their parent.
612+
**Default:** `Infinity`.
613+
614+
This function is used to create a hook running
615+
after each subtest of the current test.
616+
617+
```js
618+
test('top level test', async (t) => {
619+
t.afterEach((t) => t.diagnostics(`finished running ${t.name}`));
620+
await t.test(
621+
'This is a subtest',
622+
(t) => {
623+
assert.ok('some relevant assertion here');
624+
}
625+
);
626+
});
627+
```
628+
451629
### `context.diagnostic(message)`
452630

453631
<!-- YAML
@@ -466,6 +644,14 @@ test('top level test', (t) => {
466644
});
467645
```
468646

647+
### `context.name`
648+
649+
<!-- YAML
650+
added: REPLACEME
651+
-->
652+
653+
The name of the test
654+
469655
### `context.runOnly(shouldRunOnlyTests)`
470656

471657
<!-- YAML
@@ -564,6 +750,7 @@ changes:
564750
* `only` {boolean} If truthy, and the test context is configured to run
565751
`only` tests, then this test will be run. Otherwise, the test is skipped.
566752
**Default:** `false`.
753+
* `signal` {AbortSignal} Allows aborting an in-progress test
567754
* `skip` {boolean|string} If truthy, the test is skipped. If a string is
568755
provided, that string is displayed in the test results as the reason for
569756
skipping the test. **Default:** `false`.
@@ -604,6 +791,14 @@ An instance of `SuiteContext` is passed to each suite function in order to
604791
interact with the test runner. However, the `SuiteContext` constructor is not
605792
exposed as part of the API.
606793

794+
### `context.name`
795+
796+
<!-- YAML
797+
added: REPLACEME
798+
-->
799+
800+
The name of the suite
801+
607802
### `context.signal`
608803

609804
<!-- YAML

lib/internal/test_runner/harness.js

+11
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,19 @@ function runInParentContext(Factory) {
170170
return cb;
171171
}
172172

173+
function hook(hook) {
174+
return (fn, options) => {
175+
const parent = testResources.get(executionAsyncId()) || setup(root);
176+
parent.createHook(hook, fn, options);
177+
};
178+
}
179+
173180
module.exports = {
174181
test: FunctionPrototypeBind(test, root),
175182
describe: runInParentContext(Suite),
176183
it: runInParentContext(ItTest),
184+
before: hook('before'),
185+
after: hook('after'),
186+
beforeEach: hook('beforeEach'),
187+
afterEach: hook('afterEach'),
177188
};

0 commit comments

Comments
 (0)