Testing with Node
Built-in test runner patterns (node:test) for unit and integration tests. Results appear in the same fence: same-line // comments when short, multiline // blocks below the sample when not.
Search across all documentation pages
Built-in test runner patterns (node:test) for unit and integration tests. Results appear in the same fence: same-line // comments when short, multiline // blocks below the sample when not.
Define tests with node:test and strict asserts.
import test from "node:test";
import assert from "node:assert/strict";
test("adds", () => {
assert.equal(1 + 1, 2);
});
// node --test runs this; assertion passesGroup related cases with nested tests for clearer reports.
import test from "node:test";
import assert from "node:assert/strict";
test("user service", async (t) => {
await t.test("creates user", async () => {
assert.ok(true);
});
});
// nested name: user service > creates userMock functions and assert call counts with node:test mock helpers.
import test, { mock } from "node:test";
import assert from "node:assert/strict";
test("calls fn", () => {
const fn = mock.fn();
fn();
assert.equal(fn.mock.calls.length, 1); // 1
});Prefer dependency injection for clocks over global timer fakes when possible.
type Clock = { now: () => number };
const fixed: Clock = { now: () => 1_700_000_000_000 };
fixed.now() // 1700000000000Focus or skip tests during development - do not leave .only in CI.
import test from "node:test";
test.skip("flaky", () => {});
// test.only("debug this", () => {});
typeof test.skip // "function"Return a promise or use async functions - failures reject the test.
import test from "node:test";
import assert from "node:assert/strict";
test("loads", async () => {
const v = await Promise.resolve(1);
assert.equal(v, 1); // 1
});Shared setup/teardown with before/after hooks.
import { before, after } from "node:test";
const state = { n: 0 };
before(() => { state.n = 1; });
after(() => { state.n = 0; });
// hooks run around tests in the fileCompare objects and arrays deeply.
import assert from "node:assert/strict";
assert.deepEqual({ a: 1 }, { a: 1 });
// passes; assert.deepEqual({ a: 1 }, { a: 2 }) throwsExpect a promise rejection with message match.
import assert from "node:assert/strict";
await assert.rejects(async () => {
throw new Error("nope");
}, /nope/);
// passesPrefer injecting dependencies over module mocks when you can.
export function createApp({ fetchImpl = fetch } = {}) {
return { fetchImpl };
}
const app = createApp({ fetchImpl: (async () => new Response("ok")) as typeof fetch });
typeof app.fetchImpl // "function"Bound slow tests with a timeout option.
import test from "node:test";
test("slow", { timeout: 5_000 }, async () => {
await Promise.resolve();
});
// fails if the test exceeds 5000msSnapshots are not built-in like Jest - assert explicit values or use a small helper.
const render = (s: string) => `<${s}>`;
render("x") // "<x>"
// assert.equal(render(ui), expectedString);Execute tests with node --test globs.
// package.json: "test": "node --test dist/**/*.test.js"
const cmd = "node --test";
cmd.startsWith("node") // trueUse node --experimental-test-coverage or c8/nyc depending on your toolchain.
// node --test --experimental-test-coverage
const flag = "--experimental-test-coverage";
flag.includes("coverage") // trueLoop cases for compact matrices of inputs.
import test from "node:test";
import assert from "node:assert/strict";
for (const { in: x, out } of [{ in: 1, out: 2 }, { in: 2, out: 3 }]) {
test(`inc ${x}`, () => assert.equal(x + 1, out));
}
// two tests: inc 1, inc 2Stack versions: Node.js 24.18.0 (LTS line 24) · TypeScript 5.6+ · npm 10+
Reviewed by Chris St. John·Last updated Jul 19, 2026