← BACK

eslint-node-test

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

JavaScript ⭐ 58

eslint-node-test

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 nodeTest from 'eslint-node-test';
import {defineConfig} from 'eslint/config';

export default defineConfig([
	{
		files: [
			'**/*.js'
		],
		plugins: {
			'node-test': nodeTest
		},
		extends: [
			'node-test/recommended'
		]
	}
]);

Or configure rules individually:

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

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

The example scopes the plugin to JavaScript files. Add TypeScript extensions only when your ESLint config provides a TypeScript parser for them. 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-arguments Enforce the correct number of arguments for node:assert assertions. ✅ ☑️
consistent-assert-style Enforce a consistent truthiness assertion style. 🔧
consistent-assert-throws-callback-style Enforce a consistent body style for assert.throws() arrow callbacks. 🔧
consistent-modifier-style Enforce a consistent style for test modifiers.
consistent-test-context-name Enforce a consistent name for the test context parameter. ✅ ☑️
consistent-test-filename Enforce a consistent test file name pattern.
consistent-test-it Enforce consistent use of test or it.
hooks-order Enforce a consistent order of hook declarations. ✅ ☑️ 🔧
max-assertions Enforce a maximum number of assertions in a test.
max-nested-describe Enforce a maximum depth for nested describe blocks.
no-assert-in-describe Disallow assertions directly inside a describe body.
no-assert-in-hook Disallow assertions inside hooks.
no-assert-match-string Disallow strings as the regexp argument of assert.match()/assert.doesNotMatch(). ✅ ☑️ 💡
no-assert-throws-async Disallow passing an async function to assert.throws()/assert.doesNotThrow(). ✅ ☑️ 💡
no-assert-throws-call Disallow calling the function passed to assert.throws(). ✅ ☑️ 💡
no-assert-throws-multiple-statements Disallow multiple statements in assert.throws()/assert.rejects() callbacks.
no-assert-throws-string Disallow a string as the error matcher of assert.throws()/assert.rejects(). ✅ ☑️ 💡
no-async-describe Disallow async describe callbacks. ✅ ☑️
no-async-fn-without-await Disallow async test/hook functions that have no await expression. ✅ ☑️ 💡
no-callback-and-promise Disallow a test or hook from using both a callback and a Promise. ✅ ☑️
no-commented-tests Disallow commented-out tests.
no-compound-assertion Disallow compound truthiness assertions. ✅ ☑️ 🔧
no-conditional-assertion Disallow assertions inside conditional code within a test. ✅ ☑️
no-conditional-in-test Disallow conditional logic inside tests.
no-conditional-tests Disallow conditionally registering tests, suites, and hooks.
no-conflicting-modifiers Disallow conflicting test modifiers. ✅ ☑️
no-constant-assertion Disallow assertions with constant outcomes. ✅ ☑️
no-done-callback Disallow callback (done) parameters in tests and hooks.
no-duplicate-assertions Disallow adjacent duplicate assertions. ✅ ☑️
no-duplicate-hooks Disallow duplicate hooks within the same scope.
no-duplicate-mock-timers-enable Disallow enabling mock timers more than once without resetting them. ✅ ☑️
no-duplicate-plan Disallow setting a test plan more than once in the same test. ✅ ☑️
no-expect-failure-without-reason Require a reason when marking a test or suite as expected to fail.
no-export Disallow exports from test files.
no-identical-assertion-arguments Disallow comparing a value to itself in an assertion. ✅ ☑️
no-identical-title Disallow identical test titles within the same scope. ✅ ☑️
no-import-test-files Disallow imports of Node.js test files. ✅ ☑️
no-incorrect-deep-equal Disallow deepEqual/deepStrictEqual (and their notDeep* variants) when comparing with primitives. ✅ ☑️ 🔧
no-incorrect-strict-equal Disallow strictEqual/equal (and their not* variants) when comparing with an object or array literal. ✅ ☑️ 🔧
no-late-test-activity Disallow test activity inside detached asynchronous callbacks. ✅ ☑️ 🔧
no-loop-static-title Disallow a static test or suite title inside a loop. ✅ ☑️
no-misused-concurrency Disallow the concurrency option on a test without subtests. ✅ ☑️
no-misused-context-hook Disallow context hooks without runnable subtests. ✅ ☑️
no-mock-module-after-import Disallow mocking a module after statically importing it. ✅ ☑️
no-mock-timers-destructured-import Disallow destructured timer imports when using mock.timers. ✅ ☑️
no-nested-tests Disallow tests and suites nested inside a test body. ✅ ☑️
no-only-test Disallow the .only test modifier. ✅ ☑️ 💡
no-parent-test-context Disallow references to parent test contexts inside subtests. ✅ ☑️ 💡
no-process-chdir-in-test Disallow changing the working directory inside tests.
no-process-env-mutation Disallow mutating process.env inside tests.
no-process-exit-in-test Disallow process exit control in test files. ✅ ☑️
no-skip-test Disallow the .skip test modifier. 💡
no-skip-without-reason Require a reason when skipping or marking a test as todo.
no-skip-without-return Disallow t.skip()/t.todo() without returning afterwards. 💡
no-sleep-in-test Disallow sleeping in tests with setTimeout.
no-snapshot-in-loop Disallow snapshot assertions inside loop bodies.
no-standalone-assert Disallow assertions outside of a test. ✅ ☑️
no-test-global-configuration Disallow process-wide node:test configuration inside tests.
no-test-inside-hook Disallow defining tests and suites inside a hook. ✅ ☑️
no-test-return-statement Disallow returning a concrete non-Promise value from a test or hook.
no-todo-test Disallow the .todo test modifier. 💡
no-unawaited-promise-assertion Disallow assertions inside unawaited Promise callbacks. ✅ ☑️ 🔧
no-unawaited-rejects Require assert.rejects()/assert.doesNotReject() to be awaited or returned. ✅ ☑️ 🔧
no-unawaited-subtest Require subtests created with the test context to be awaited or returned. 🔧
no-unknown-test-options Disallow unknown options in test and hook option objects.
no-unneeded-async-rejects-callback Disallow unneeded async callbacks passed to assert.rejects(). 💡
no-useless-assertion Disallow assert.doesNotThrow() and assert.doesNotReject().
prefer-assert-match Prefer assert.match()/assert.doesNotMatch() over asserting RegExp#test() / String#match() results. ✅ ☑️ 🔧
prefer-assert-throws Prefer assert.throws()/assert.rejects() over try/catch with an assertion.
prefer-async-await Prefer async/await over returning a Promise.
prefer-context-mock Prefer the test context t.mock over the global mock.
prefer-diagnostic Prefer the test context diagnostic() over console inside tests. 💡
prefer-equality-assertion Prefer an equality assertion over a truthiness assertion on a comparison. ✅ ☑️ 🔧
prefer-hooks-on-top Require hooks to be declared before the tests in their scope.
prefer-lowercase-title Enforce lowercase test titles. 🔧
prefer-mock-accessor Prefer mock.getter() and mock.setter() over mock.method() with accessor options. 🔧
prefer-mock-call-count Prefer mock.callCount() over mock.calls.length. ✅ ☑️ 🔧
prefer-mock-method Prefer mock.method() over assigning mock.fn() to an object property. 💡
prefer-strict-assert Prefer strict assertion methods over their legacy loose counterparts. ✅ ☑️ 🔧
prefer-test-context-assert Prefer the test context t.assert over the imported node:assert. 💡
prefer-todo Prefer .todo for empty placeholder tests. 💡
require-assertion Require that each test contains at least one assertion.
require-await-concurrent-subtests Require subtests created in a loop callback to be awaited. ✅ ☑️
require-context-assert-with-plan Require assertions to use the test context when the test sets a plan. ✅ ☑️
require-hook Require setup and teardown code to be inside a hook.
require-mock-timers-advance Require mock timers to be advanced after enabling timer APIs.
require-mock-timers-apis Require an explicit apis option when enabling mock.timers. ✅ ☑️
require-throws-expectation Require an error matcher for assert.throws()/assert.rejects(). ✅ ☑️
require-throws-validator-return-true Require validator functions in assert.throws()/assert.rejects() to return true. ✅ ☑️
require-top-level-describe Require tests and hooks to be inside a top-level describe.
test-title Require tests to have a title. 🔧
test-title-format Require test titles to match a configured pattern.
valid-describe-callback Enforce valid describe callbacks. ✅ ☑️
valid-test-tags Require 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 nodeTest from 'eslint-node-test';
import {defineConfig} from 'eslint/config';

export default defineConfig([
	{
		files: [
			'**/*.js'
		],
		plugins: {
			'node-test': nodeTest
		},
		extends: [
			'node-test/recommended'
		]
	}
]);

Related