Order of nested vs outer `afterEach` hooks is unexpected and inconsistent with `after`
Version
21.6.1
Platform
darwin
Subsystem
test runner
What steps will reproduce the bug?
Create a file called hooks.mjs containing:
import { describe, afterEach, after, it } from 'node:test';
describe('parent', () => {
afterEach(() => console.log('parent after each'));
after(() => console.log('parent after'));
describe('child', () => {
afterEach(() => console.log('child after each'));
after(() => console.log('child after'));
it('works', () => {});
});
});
and run node --test hooks.mjs.
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior? Why is that the expected behavior?
child after each
parent after each
child after
parent after
<test results>
As with mocha, qunit, jest, vitest, and probably others, a child suite's afterEach hooks should be called before those of parent suites, not after them. Also, the execution order of parent/child afterEach hooks should match the order of parent/child after hooks.
Currently, after hooks correctly run child-first, but afterEach hooks run incorrectly parent-first.
What do you see instead?
parent after each
child after each
child after
parent after
<test results>
Additional information
This also reproduces with the test()/subtest API -- a test's afterEach hook will run before a subtest's afterEach hook.
I agree subtest after and afterEach should be called before parent @nodejs/test_runner.
I couldnt reproduce with testthough.
This works fine:
'use strict';
require('../common');
const assert = require('node:assert');
const { test, afterEach, after } = require('node:test');
let afterEachRacing = true;
let afterRacing = true;
test('parent', () => {
afterEach(() => afterEachRacing = false);
after(() => afterRacing = false);
test('child', () => {
afterEach(() => assert.ok(afterEachRacing));
after(() => assert.ok(afterRacing));
test(() => { });
});
});
this fails:
'use strict';
require('../common');
const assert = require('node:assert');
const { describe, it, afterEach, after } = require('node:test');
let afterEachRacing = true;
let afterRacing = true;
describe('parent', () => {
afterEach(() => afterEachRacing = false);
after(() => afterRacing = false);
describe('child', () => {
afterEach(() => assert.ok(afterEachRacing));
after(() => assert.ok(afterRacing));
it(() => { });
});
});
I meant that
test('parent', async (t) => {
t.afterEach((c) => console.log('parent after each', c.name));
t.after((c) => console.log('parent after', c.name));
await t.test('child', async (t) => {
t.afterEach((c) => console.log('child after each', c.name));
t.after((c) => console.log('child after', c.name));
await t.test('works', () => {});
});
});
produces
parent after each works
child after each works
parent after each child
child after child
parent after parent
instead of
child after each works
parent after each works
parent after each child
child after child
parent after parent
so the parent's afterEach for works runs before the child's.