Skip to content

Commit e97eefa

Browse files
atlowChemiMoLow
authored andcommitted
test_runner: execute before hook on test
Execute the before hook for Test as well, and execute it on root before executing any tests. Fixes: #47518 PR-URL: #47586 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
1 parent 980bf05 commit e97eefa

File tree

4 files changed

+107
-16
lines changed

4 files changed

+107
-16
lines changed

doc/api/test.md

+24
Original file line numberDiff line numberDiff line change
@@ -1429,12 +1429,36 @@ Emitted when a test starts.
14291429

14301430
<!-- YAML
14311431
added: v18.0.0
1432+
changes:
1433+
- version: REPLACEME
1434+
pr-url: https://github.com/nodejs/node/pull/47586
1435+
description: The `before` function was added to TestContext.
14321436
-->
14331437

14341438
An instance of `TestContext` is passed to each test function in order to
14351439
interact with the test runner. However, the `TestContext` constructor is not
14361440
exposed as part of the API.
14371441

1442+
### `context.before([fn][, options])`
1443+
1444+
<!-- YAML
1445+
added: REPLACEME
1446+
-->
1447+
1448+
* `fn` {Function|AsyncFunction} The hook function. The first argument
1449+
to this function is a [`TestContext`][] object. If the hook uses callbacks,
1450+
the callback function is passed as the second argument. **Default:** A no-op
1451+
function.
1452+
* `options` {Object} Configuration options for the hook. The following
1453+
properties are supported:
1454+
* `signal` {AbortSignal} Allows aborting an in-progress hook.
1455+
* `timeout` {number} A number of milliseconds the hook will fail after.
1456+
If unspecified, subtests inherit this value from their parent.
1457+
**Default:** `Infinity`.
1458+
1459+
This function is used to create a hook running before
1460+
subtest of the current test.
1461+
14381462
### `context.beforeEach([fn][, options])`
14391463

14401464
<!-- YAML

lib/internal/test_runner/test.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ class TestContext {
127127
return subtest.start();
128128
}
129129

130+
before(fn, options) {
131+
this.#test.createHook('before', fn, options);
132+
}
133+
130134
after(fn, options) {
131135
this.#test.createHook('after', fn, options);
132136
}
@@ -414,6 +418,9 @@ class Test extends AsyncResource {
414418
validateOneOf(name, 'hook name', kHookNames);
415419
// eslint-disable-next-line no-use-before-define
416420
const hook = new TestHook(fn, options);
421+
if (name === 'before') {
422+
hook.run = runOnce(hook.run);
423+
}
417424
ArrayPrototypePush(this.hooks[name], hook);
418425
return hook;
419426
}
@@ -525,6 +532,9 @@ class Test extends AsyncResource {
525532
if (this.parent?.hooks.beforeEach.length > 0) {
526533
await this.parent.runHook('beforeEach', { args, ctx });
527534
}
535+
if (this.parent?.hooks.before.length > 0) {
536+
await this.parent.runHook('before', this.parent.getRunArgs());
537+
}
528538
const stopPromise = stopTest(this.timeout, this.signal);
529539
const runArgs = ArrayPrototypeSlice(args);
530540
ArrayPrototypeUnshift(runArgs, this.fn, ctx);
@@ -561,7 +571,7 @@ class Test extends AsyncResource {
561571
this.pass();
562572
} catch (err) {
563573
try { await after(); } catch { /* Ignore error. */ }
564-
try { await afterEach(); } catch { /* test is already failing, let's the error */ }
574+
try { await afterEach(); } catch { /* test is already failing, let's ignore the error */ }
565575
if (isTestFailureError(err)) {
566576
if (err.failureType === kTestTimeoutFailure) {
567577
this.#cancel(err);
@@ -793,7 +803,7 @@ class Suite extends Test {
793803

794804
this.pass();
795805
} catch (err) {
796-
try { await afterEach(); } catch { /* test is already failing, let's the error */ }
806+
try { await afterEach(); } catch { /* test is already failing, let's ignore the error */ }
797807
if (isTestFailureError(err)) {
798808
this.fail(err);
799809
} else {

test/fixtures/test-runner/output/hooks.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const common = require('../../../common');
44
const assert = require('assert');
55
const { test, describe, it, before, after, beforeEach, afterEach } = require('node:test');
66

7+
before((t) => t.diagnostic('before 1 called'));
8+
79
describe('describe hooks', () => {
810
const testArr = [];
911
before(function() {
@@ -91,6 +93,7 @@ describe('afterEach throws and test fails', () => {
9193
test('test hooks', async (t) => {
9294
const testArr = [];
9395

96+
t.before((t) => testArr.push('before ' + t.name));
9497
t.after(common.mustCall((t) => testArr.push('after ' + t.name)));
9598
t.beforeEach((t) => testArr.push('beforeEach ' + t.name));
9699
t.afterEach((t) => testArr.push('afterEach ' + t.name));
@@ -105,7 +108,7 @@ test('test hooks', async (t) => {
105108
});
106109

107110
assert.deepStrictEqual(testArr, [
108-
'beforeEach 1', '1', 'afterEach 1',
111+
'beforeEach 1', 'before test hooks', '1', 'afterEach 1',
109112
'beforeEach 2', '2', 'afterEach 2',
110113
'beforeEach nested',
111114
'nested beforeEach nested 1', 'nested1', 'nested afterEach nested 1',
@@ -114,6 +117,13 @@ test('test hooks', async (t) => {
114117
]);
115118
});
116119

120+
test('t.before throws', async (t) => {
121+
t.after(common.mustCall());
122+
t.before(() => { throw new Error('before'); });
123+
await t.test('1', () => {});
124+
await t.test('2', () => {});
125+
});
126+
117127
test('t.beforeEach throws', async (t) => {
118128
t.after(common.mustCall());
119129
t.beforeEach(() => { throw new Error('beforeEach'); });
@@ -149,3 +159,5 @@ test('t.after() is called if test body throws', (t) => {
149159
});
150160
throw new Error('bye');
151161
});
162+
163+
before((t) => t.diagnostic('before 2 called'));

test/fixtures/test-runner/output/hooks.snapshot

+58-13
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ not ok 2 - before throws
6767
*
6868
*
6969
*
70+
*
7071
...
7172
# Subtest: after throws
7273
# Subtest: 1
@@ -307,6 +308,53 @@ ok 8 - test hooks
307308
---
308309
duration_ms: *
309310
...
311+
# Subtest: t.before throws
312+
# Subtest: 1
313+
not ok 1 - 1
314+
---
315+
duration_ms: *
316+
failureType: 'hookFailed'
317+
error: 'failed running before hook'
318+
code: 'ERR_TEST_FAILURE'
319+
stack: |-
320+
*
321+
*
322+
*
323+
*
324+
*
325+
*
326+
*
327+
*
328+
*
329+
*
330+
...
331+
# Subtest: 2
332+
not ok 2 - 2
333+
---
334+
duration_ms: *
335+
failureType: 'hookFailed'
336+
error: 'failed running before hook'
337+
code: 'ERR_TEST_FAILURE'
338+
stack: |-
339+
*
340+
*
341+
*
342+
*
343+
*
344+
*
345+
*
346+
*
347+
*
348+
*
349+
...
350+
1..2
351+
not ok 9 - t.before throws
352+
---
353+
duration_ms: *
354+
failureType: 'subtestsFailed'
355+
error: '2 subtests failed'
356+
code: 'ERR_TEST_FAILURE'
357+
...
310358
# Subtest: t.beforeEach throws
311359
# Subtest: 1
312360
not ok 1 - 1
@@ -347,7 +395,7 @@ ok 8 - test hooks
347395
*
348396
...
349397
1..2
350-
not ok 9 - t.beforeEach throws
398+
not ok 10 - t.beforeEach throws
351399
---
352400
duration_ms: *
353401
failureType: 'subtestsFailed'
@@ -394,7 +442,7 @@ not ok 9 - t.beforeEach throws
394442
*
395443
...
396444
1..2
397-
not ok 10 - t.afterEach throws
445+
not ok 11 - t.afterEach throws
398446
---
399447
duration_ms: *
400448
failureType: 'subtestsFailed'
@@ -419,15 +467,14 @@ not ok 10 - t.afterEach throws
419467
*
420468
*
421469
*
422-
*
423470
...
424471
# Subtest: 2
425472
ok 2 - 2
426473
---
427474
duration_ms: *
428475
...
429476
1..2
430-
not ok 11 - afterEach when test fails
477+
not ok 12 - afterEach when test fails
431478
---
432479
duration_ms: *
433480
failureType: 'subtestsFailed'
@@ -452,7 +499,6 @@ not ok 11 - afterEach when test fails
452499
*
453500
*
454501
*
455-
*
456502
...
457503
# Subtest: 2
458504
not ok 2 - 2
@@ -474,15 +520,15 @@ not ok 11 - afterEach when test fails
474520
*
475521
...
476522
1..2
477-
not ok 12 - afterEach throws and test fails
523+
not ok 13 - afterEach throws and test fails
478524
---
479525
duration_ms: *
480526
failureType: 'subtestsFailed'
481527
error: '2 subtests failed'
482528
code: 'ERR_TEST_FAILURE'
483529
...
484530
# Subtest: t.after() is called if test body throws
485-
not ok 13 - t.after() is called if test body throws
531+
not ok 14 - t.after() is called if test body throws
486532
---
487533
duration_ms: *
488534
failureType: 'testCodeFailure'
@@ -493,16 +539,15 @@ not ok 13 - t.after() is called if test body throws
493539
*
494540
*
495541
*
496-
*
497-
*
498-
*
499542
...
500543
# - after() called
501-
1..13
502-
# tests 35
544+
1..14
545+
# before 1 called
546+
# before 2 called
547+
# tests 38
503548
# suites 8
504549
# pass 14
505-
# fail 19
550+
# fail 22
506551
# cancelled 2
507552
# skipped 0
508553
# todo 0

0 commit comments

Comments
 (0)