Open-Source Wikis

/

Node.js

/

Systems

/

Test runner

nodejs/node

Test runner

Owners: @nodejs/test_runner. Public docs at doc/api/test.md.

Purpose

Provide a built-in test framework — node --test and node:test — that runs without installing third-party packages: parallel execution, watch mode, mocking, snapshots, code coverage, TAP/dot/junit/spec reporters, and concurrency primitives.

Directory layout

lib/
  test.js                                  // public node:test module
  test/
    reporters.js                           // re-export of reporter modules
  internal/test_runner/
    runner.js                              // top-level driver: discover, spawn, aggregate
    test.js                                // Test/Suite/Hook classes
    harness.js                             // describe/it/before/after globals
    coverage.js                            // V8 coverage integration
    snapshot.js                            // snapshot module (toMatchSnapshot)
    tests_stream.js                        // events stream consumed by reporters
    utils.js                               // shared helpers
    assert.js                              // strict-style assertion bridge
    mock/                                  // mock module + module-mock
    reporter/                              // tap, dot, spec, junit, lcov reporters
lib/internal/main/test_runner.js           // entry: node --test
lib/internal/main/watch_mode.js            // entry: node --watch / --watch-path with --test

Key abstractions

Type / file Role
Test (lib/internal/test_runner/test.js) Single test or suite node in the tree.
Suite (lib/internal/test_runner/test.js) Container test that holds children and hooks.
TestContext (lib/internal/test_runner/test.js) t.test, t.before, t.diagnostic, t.runOnly, etc. given to test fns.
Runner (lib/internal/test_runner/runner.js) Discovers files (matching --test-name-pattern etc.), spawns child Nodes.
TestsStream (lib/internal/test_runner/tests_stream.js) Readable that emits TAP-shape events.
Reporters (lib/internal/test_runner/reporter/) Subscribe to TestsStream and emit text.
MockModule (lib/internal/test_runner/mock/) t.mock.module(specifier) implementation.
Coverage (lib/internal/test_runner/coverage.js) Wraps V8 coverage and produces lcov/text/json reports.

How it works

sequenceDiagram
    participant CLI as node --test [paths]
    participant Runner as Runner
    participant Discover as discover files
    participant Worker as Worker / fork per file
    participant Stream as TestsStream
    participant Reporter as TAP / spec / lcov

    CLI->>Runner: lib/internal/main/test_runner.js
    Runner->>Discover: glob test files
    Discover-->>Runner: file list
    loop per concurrency
        Runner->>Worker: spawn child Node, --test-isolation=process
        Worker-->>Stream: emit start/pass/fail/diagnostic
    end
    Stream->>Reporter: pipe events
    Reporter-->>CLI: stdout

Without --test-isolation=process, all tests run in the parent Node process; --test-isolation=process (the default in 22+) spawns one Node child per file. Watch mode (--watch) re-runs only the affected files when their dependency graph changes (graph derived from CJS require.cache and ESM module map).

Mocking

t.mock provides:

  • t.mock.fn(impl) — function mock with call tracking.
  • t.mock.method(obj, name, impl) — replace a method.
  • t.mock.timers.enable({ apis: ['setTimeout', 'setInterval', ...] }) — fake timers.
  • t.mock.module(specifier, opts) — replace a module's exports for the duration of the test (uses the module.register hooks).

The module-mock implementation (lib/internal/test_runner/mock/module.js) registers a customization-hook layer that intercepts import/require to swap the target module.

Coverage

--experimental-test-coverage enables V8 coverage collection. lib/internal/test_runner/coverage.js:

  1. Calls inspector Profiler.startPreciseCoverage.
  2. Maps V8 coverage ranges back to source coordinates.
  3. Emits lcov/text/json/junit-coverage reports.

The legacy c8 integration is documented in doc/api/test.md.

Snapshots

t.assert.snapshot (and t.toMatchSnapshot) live in lib/internal/test_runner/snapshot.js. Serialised values get diffed against a *.snapshot file next to the test file; --test-update-snapshots rewrites them.

Reporters

Each reporter is a Transform stream from TestsStream's structured events to text. Supported: tap (default), dot, spec, junit, lcov (for coverage). Custom reporters are written as transforms exported by user modules.

Integration points

  • Module loaders: mocking integrates with module.register; watch mode reads the loader's module graph.
  • Inspector / V8 profiler: coverage uses the Inspector API.
  • Streams: TestsStream is a regular Readable.
  • Worker threads: tests can spin up workers; the runner does not by itself.

Entry points for modification

  • New reporter? Add a module under lib/internal/test_runner/reporter/ and register it in lib/test/reporters.js.
  • New mock primitive? lib/internal/test_runner/mock/.
  • Change discovery behaviour? lib/internal/test_runner/runner.js#parseCommandLine and glob.
  • Affected tests live at test/parallel/test-runner-*.js and test/test-runner/.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Test runner – Node.js wiki | Factory