Built-in Test Runner
node:test — batteries included
Section titled “node:test — batteries included”Since Node 18, you can write and run tests with zero third-party dependencies. The node:test module provides test(), describe(), it(), before/after hooks, and TAP-compatible output. Pair it with node:assert for assertions.
Basic test anatomy
Section titled “Basic test anatomy”A test file is just a .js (or .mjs) file that imports node:test and node:assert:
import { test, describe, it } from 'node:test';import assert from 'node:assert/strict';
test('add returns the correct sum', function() { assert.equal(2 + 3, 5);});Run it from the terminal:
node --test math.test.jsOr run all *.test.js files recursively:
node --testdescribe / it — grouping related tests
Section titled “describe / it — grouping related tests”describe groups related tests. it is an alias for test that reads naturally inside describe:
import { describe, it } from 'node:test';import assert from 'node:assert/strict';
describe('String utilities', function() { it('trim removes leading and trailing whitespace', function() { assert.equal(' hello '.trim(), 'hello'); });
it('split divides by delimiter', function() { assert.deepEqual('a,b,c'.split(','), ['a', 'b', 'c']); });});node --test string-utils.test.js# TAP output:# ok 1 - String utilities > trim removes leading and trailing whitespace# ok 2 - String utilities > split divides by delimiterAsync tests
Section titled “Async tests”Pass an async function to test or it. The runner awaits it — a rejected promise fails the test:
import { test } from 'node:test';import assert from 'node:assert/strict';
test('fetch-like async test', async function() { const result = await Promise.resolve(42); assert.equal(result, 42);});node:assert/strict cheat sheet
Section titled “node:assert/strict cheat sheet”import assert from 'node:assert/strict';
assert.equal(actual, expected); // == (strict: ===)assert.notEqual(actual, expected); // !==assert.deepEqual(obj1, obj2); // deep structural equalityassert.notDeepEqual(obj1, obj2);assert.throws(fn, /pattern/); // fn must throw matching errorassert.rejects(asyncFn, /pattern/); // async fn must rejectassert.ok(value); // truthy checkAlways prefer node:assert/strict over node:assert — the strict variant uses === for scalar comparisons and avoids subtle type coercion surprises.
Live runnable demo
Section titled “Live runnable demo”The snippet below is a self-contained node:test mini-suite. It runs in node mode so you see real TAP output:
Needs the Node.js runtime — open in StackBlitz to run.
Beyond the basics
Section titled “Beyond the basics”# Watch mode — re-runs on file change (Node 22+)node --test --watch
# Only run tests matching a patternnode --test --test-name-pattern "Array"
# Code coverage report (Node 22+)node --test --experimental-test-coverage
# Parallel test files (default in Node 22+)node --test --test-concurrency=4