no-conditional-tests
July 2, 2026 Β· View on GitHub
π Disallow conditionally registering tests, suites, and hooks.
πΌπ« This rule is enabled in the β
recommended config. This rule is disabled in the βοΈ unopinionated config.
Registering a test, suite, or hook inside a condition makes the suite structure depend on runtime state, so the tests, setup, and teardown that run vary between environments. A test silently skipped by a condition looks the same as a passing run, and a skipped hook can make setup or teardown silently differ. Put the condition inside the test or hook body, or split the file by environment instead.
This rule reports a test/it/describe/suite/before/after/beforeEach/afterEach call guarded by an if, else, ternary, logical (&&/||/??), or switch. Loops are allowed, since iterating to register tests is the idiomatic way to write table-driven tests in node:test. The rule only checks lexical ancestors of the registration call and does not trace callbacks passed to helper methods.
Examples
import {test, describe, beforeEach} from 'node:test';
// β
if (process.env.CI) {
test('runs only on CI', () => {});
}
process.platform === 'darwin' && test('macOS only', () => {});
if (process.env.CI) {
beforeEach(() => {
startServer();
});
}
// β
(condition inside the test)
test('skips off CI', t => {
if (!process.env.CI) {
t.skip('CI only');
return;
}
assert.ok(check());
});
// β
(table-driven via a loop)
for (const testCase of cases) {
test(testCase.name, () => {
assert.strictEqual(run(testCase.input), testCase.output);
});
}