eslint-node-test

July 13, 2026 Β· View on GitHub

ESLint rules for the Node.js built-in test runner (node:test)

This plugin helps you avoid common mistakes and write more consistent tests with node:test, the test runner built into Node.js.

Install

npm install --save-dev eslint eslint-node-test

Requires ESLint >=10.4, flat config, and ESM.

Usage

Use a preset config or configure each rule in eslint.config.js.

import eslintNodeTest from 'eslint-node-test';
import {defineConfig} from 'eslint/config';

export default defineConfig([
	eslintNodeTest.configs.recommended,
]);

Or configure rules individually:

import eslintNodeTest from 'eslint-node-test';
import {defineConfig} from 'eslint/config';

export default defineConfig([
	{
		plugins: {
			'node-test': eslintNodeTest,
		},
		rules: {
			'node-test/no-only-test': 'error',
			'node-test/no-identical-title': 'error',
		},
	},
]);

The rules only activate in files that import from node:test (and assertion rules also activate for Node's built-in assert module), so you can safely apply the plugin across your whole project.

Rules

πŸ’Ό Configurations enabled in.
βœ… Set in the recommended configuration.
β˜‘οΈ Set in the unopinionated configuration.
πŸ”§ Automatically fixable by the --fix CLI option.
πŸ’‘ Manually fixable by editor suggestions.

NameΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β DescriptionπŸ’ΌπŸ”§πŸ’‘
assertion-argumentsEnforce the correct number of arguments for node:assert assertions.βœ… β˜‘οΈ
consistent-assert-styleEnforce a consistent truthiness assertion style.βœ…πŸ”§
consistent-assert-throws-callback-styleEnforce a consistent body style for assert.throws() arrow callbacks.πŸ”§
consistent-modifier-styleEnforce a consistent style for test modifiers.
consistent-test-context-nameEnforce a consistent name for the test context parameter.βœ… β˜‘οΈ
consistent-test-filenameEnforce a consistent test file name pattern.
consistent-test-itEnforce consistent use of test or it.
hooks-orderEnforce a consistent order of hook declarations.βœ… β˜‘οΈπŸ”§
max-assertionsEnforce a maximum number of assertions in a test.
max-nested-describeEnforce a maximum depth for nested describe blocks.βœ…
no-assert-in-describeDisallow assertions directly inside a describe body.βœ…
no-assert-in-hookDisallow assertions inside hooks.
no-assert-match-stringDisallow strings as the regexp argument of assert.match()/assert.doesNotMatch().βœ… β˜‘οΈπŸ’‘
no-assert-throws-asyncDisallow passing an async function to assert.throws()/assert.doesNotThrow().βœ… β˜‘οΈπŸ’‘
no-assert-throws-callDisallow calling the function passed to assert.throws().βœ… β˜‘οΈπŸ’‘
no-assert-throws-multiple-statementsDisallow multiple statements in assert.throws()/assert.rejects() callbacks.βœ…
no-assert-throws-stringDisallow a string as the error matcher of assert.throws()/assert.rejects().βœ… β˜‘οΈπŸ’‘
no-async-describeDisallow async describe callbacks.βœ… β˜‘οΈ
no-async-fn-without-awaitDisallow async test/hook functions that have no await expression.βœ… β˜‘οΈπŸ’‘
no-callback-and-promiseDisallow a test or hook from using both a callback and a Promise.βœ… β˜‘οΈ
no-commented-testsDisallow commented-out tests.
no-compound-assertionDisallow compound truthiness assertions.βœ… β˜‘οΈπŸ”§
no-conditional-assertionDisallow assertions inside conditional code within a test.βœ… β˜‘οΈ
no-conditional-in-testDisallow conditional logic inside tests.
no-conditional-testsDisallow conditionally registering tests, suites, and hooks.βœ…
no-conflicting-modifiersDisallow conflicting test modifiers.βœ… β˜‘οΈ
no-constant-assertionDisallow assertions with constant outcomes.βœ… β˜‘οΈ
no-done-callbackDisallow callback (done) parameters in tests and hooks.
no-duplicate-assertionsDisallow adjacent duplicate assertions.βœ… β˜‘οΈ
no-duplicate-hooksDisallow duplicate hooks within the same scope.βœ…
no-duplicate-mock-timers-enableDisallow enabling mock timers more than once without resetting them.βœ… β˜‘οΈ
no-duplicate-planDisallow setting a test plan more than once in the same test.βœ… β˜‘οΈ
no-expect-failure-without-reasonRequire a reason when marking a test or suite as expected to fail.βœ…
no-exportDisallow exports from test files.βœ…
no-identical-assertion-argumentsDisallow comparing a value to itself in an assertion.βœ… β˜‘οΈ
no-identical-titleDisallow identical test titles within the same scope.βœ… β˜‘οΈ
no-import-test-filesDisallow imports of Node.js test files.βœ… β˜‘οΈ
no-incorrect-deep-equalDisallow deepEqual/deepStrictEqual (and their notDeep* variants) when comparing with primitives.βœ… β˜‘οΈπŸ”§
no-incorrect-strict-equalDisallow strictEqual/equal (and their not* variants) when comparing with an object or array literal.βœ… β˜‘οΈπŸ”§
no-late-test-activityDisallow test activity inside detached asynchronous callbacks.βœ… β˜‘οΈπŸ”§
no-loop-static-titleDisallow a static test or suite title inside a loop.βœ… β˜‘οΈ
no-misused-concurrencyDisallow the concurrency option on a test without subtests.βœ… β˜‘οΈ
no-misused-context-hookDisallow context hooks without runnable subtests.βœ… β˜‘οΈ
no-mock-module-after-importDisallow mocking a module after statically importing it.βœ… β˜‘οΈ
no-mock-timers-destructured-importDisallow destructured timer imports when using mock.timers.βœ… β˜‘οΈ
no-nested-testsDisallow tests and suites nested inside a test body.βœ… β˜‘οΈ
no-only-testDisallow the .only test modifier.βœ… β˜‘οΈπŸ’‘
no-parent-test-contextDisallow references to parent test contexts inside subtests.βœ… β˜‘οΈπŸ’‘
no-process-chdir-in-testDisallow changing the working directory inside tests.βœ…
no-process-env-mutationDisallow mutating process.env inside tests.βœ…
no-process-exit-in-testDisallow process exit control in test files.βœ… β˜‘οΈ
no-skip-testDisallow the .skip test modifier.βœ…πŸ’‘
no-skip-without-reasonRequire a reason when skipping or marking a test as todo.
no-skip-without-returnDisallow t.skip()/t.todo() without returning afterwards.βœ…πŸ’‘
no-sleep-in-testDisallow sleeping in tests with setTimeout.
no-snapshot-in-loopDisallow snapshot assertions inside loop bodies.
no-standalone-assertDisallow assertions outside of a test.βœ… β˜‘οΈ
no-test-global-configurationDisallow process-wide node:test configuration inside tests.βœ…
no-test-inside-hookDisallow defining tests and suites inside a hook.βœ… β˜‘οΈ
no-test-return-statementDisallow returning a concrete non-Promise value from a test or hook.βœ…
no-todo-testDisallow the .todo test modifier.πŸ’‘
no-unawaited-promise-assertionDisallow assertions inside unawaited Promise callbacks.βœ… β˜‘οΈπŸ”§
no-unawaited-rejectsRequire assert.rejects()/assert.doesNotReject() to be awaited or returned.βœ… β˜‘οΈπŸ”§
no-unawaited-subtestRequire subtests created with the test context to be awaited or returned.βœ…πŸ”§
no-unknown-test-optionsDisallow unknown options in test and hook option objects.βœ…
no-unneeded-async-rejects-callbackDisallow unneeded async callbacks passed to assert.rejects().πŸ’‘
no-useless-assertionDisallow assert.doesNotThrow() and assert.doesNotReject().βœ…
prefer-assert-matchPrefer assert.match()/assert.doesNotMatch() over asserting RegExp#test() / String#match() results.βœ… β˜‘οΈπŸ”§
prefer-assert-throwsPrefer assert.throws()/assert.rejects() over try/catch with an assertion.βœ…
prefer-async-awaitPrefer async/await over returning a Promise.βœ…
prefer-context-mockPrefer the test context t.mock over the global mock.βœ…
prefer-diagnosticPrefer the test context diagnostic() over console inside tests.πŸ’‘
prefer-equality-assertionPrefer an equality assertion over a truthiness assertion on a comparison.βœ… β˜‘οΈπŸ”§
prefer-hooks-on-topRequire hooks to be declared before the tests in their scope.βœ…
prefer-lowercase-titleEnforce lowercase test titles.πŸ”§
prefer-mock-accessorPrefer mock.getter() and mock.setter() over mock.method() with accessor options.βœ…πŸ”§
prefer-mock-call-countPrefer mock.callCount() over mock.calls.length.βœ… β˜‘οΈπŸ”§
prefer-mock-methodPrefer mock.method() over assigning mock.fn() to an object property.βœ…πŸ’‘
prefer-strict-assertPrefer strict assertion methods over their legacy loose counterparts.βœ… β˜‘οΈπŸ”§
prefer-test-context-assertPrefer the test context t.assert over the imported node:assert.βœ…πŸ’‘
prefer-todoPrefer .todo for empty placeholder tests.βœ…πŸ’‘
require-assertionRequire that each test contains at least one assertion.βœ…
require-await-concurrent-subtestsRequire subtests created in a loop callback to be awaited.βœ… β˜‘οΈ
require-context-assert-with-planRequire assertions to use the test context when the test sets a plan.βœ… β˜‘οΈ
require-hookRequire setup and teardown code to be inside a hook.
require-mock-timers-advanceRequire mock timers to be advanced after enabling timer APIs.βœ…
require-mock-timers-apisRequire an explicit apis option when enabling mock.timers.βœ… β˜‘οΈ
require-throws-expectationRequire an error matcher for assert.throws()/assert.rejects().βœ… β˜‘οΈ
require-throws-validator-return-trueRequire validator functions in assert.throws()/assert.rejects() to return true.βœ… β˜‘οΈ
require-top-level-describeRequire tests and hooks to be inside a top-level describe.
test-titleRequire tests to have a title.βœ…πŸ”§
test-title-formatRequire test titles to match a configured pattern.
valid-describe-callbackEnforce valid describe callbacks.βœ… β˜‘οΈ
valid-test-tagsRequire valid test tags.βœ… β˜‘οΈπŸ”§

Preset configs

This plugin exports these configs:

  • recommended β€” Enables the recommended rules.
  • unopinionated β€” A subset of recommended with only the most uncontroversial rules.
  • all β€” Enables every rule (not recommended for general use; useful to discover new rules).
import eslintNodeTest from 'eslint-node-test';
import {defineConfig} from 'eslint/config';

export default defineConfig([
	eslintNodeTest.configs.recommended,
]);