node:test
This guide covers node:test 22.0 or newer. You need Node.js 22.12 or newer to run test-inventory and an existing JavaScript or TypeScript test suite. The scanner reads your source without executing your tests or starting a browser. These are minimum supported versions; CI comparisons list tested versions.
Choose another framework · Use an AI client
Install
npm install --save-dev test-inventoryScan your suite
Run from the folder containing your project's package.json:
npx test-inventory --framework node --pretty --output inventory.jsonTo select files explicitly, use quoted globs. This example matches the pricing-service fixture used below; adjust the globs to your suite's layout:
npx test-inventory "test/**/*.js" "src/**/*.test.js" --framework node --output inventory.jsonThe command replaces inventory.json once the new output is complete. It does not change your test files.
Check the result
Print the counts and diagnostics from the saved file:
node -e "const i = require('./inventory.json'); console.log(i.summary); console.log(i.diagnostics)"For matching tests, summary.fileCount and summary.testCount should be greater than zero. Review diagnostics even when the command exits with 0: errors can mean tests are missing. If the result is empty or unexpected, use troubleshooting.
The inventory describes source declarations, not execution results. Conditional skips are not proof that a run skipped the test. Loops and tables are one record by default; supported cases can be expanded with --expand-cases. Expected-to-fail tests execute and check for failure; they are not disabled tests.
Tests created by unreadable helpers can be missing without a diagnostic. A dependency hash does not prove complete coverage of runtime dependencies. Read the shared limits before using scans to make CI decisions.
Find skipped tests
npx test-inventory --framework node --state skip --fields fullName,state,condition,reason --prettyThis uses the same file selection as the first scan. If you supplied globs, --root or --framework-version, repeat those options here.
This selects source declarations marked skip; it is not a report of a test run. See query recipes for other inventory queries.
Example
pricing-service tests a quote service with Node.js's own runner, node:test, run by node --test from its test script. A test runs subtests with t.test(), and a subtest can skip itself.
// test/quotes.test.js
test('reprices a basket', { timeout: 2_000 }, async (t) => {
await t.test('reprices equities', () => {
// ...
await t.test('reprices bonds', async (bonds) => {
if (!process.env.BOND_FEED) bonds.skip('no bond feed');npx test-inventory "test/**/*.js" "src/**/*.test.js" --framework node --output inventory.jsonWithout the globs and --framework, the scan finds the same files from Node.js's default patterns, and node:test from the test script. A subtest is listed under its parent's title, with the id of reprices a basket in parentTestId:
{
"parentTestId": "76080f80b7409d6f",
"fullName": "reprices a basket > reprices bonds",
"state": "skip",
"isConditional": true,
"condition": "!process.env.BOND_FEED",
"reason": "no bond feed"
}Configuration and limits
Use --framework node for node:test. The scanner itself still needs Node.js 22.12 or newer. There is no config file the scan reads: node.config.json holds no file selection and Node.js reads it only with --config-file. File selection comes from supported node --test package scripts or explicit globs.
Script --import, --require and --test-global-setup files enter dependency hashes. They are not executed. The inventory lists tests inside skipped suites; the runner may report only the enclosing skipped entry. isOnly records the source modifier, while node:test applies it only with --test-only.
Exact file selection
For node:test: the files the node --test commands of the test script give, with those of the scripts it runs through npm run, pnpm, yarn, bun run, npm-run-all, run-s or run-p, or when it runs none, of the first script that runs node --test, each a glob or a path, and from Node.js 26.8 a folder, for the default patterns inside it; without any, Node.js's default patterns, **/*.test.*, **/*-test.*, **/*_test.*, **/test-*.*, **/test.* and **/test/**/*.*, with the extensions js, mjs and cjs, and from Node.js 22.18, which strips types by default, ts, mts and cts. Dot folders and node_modules are left out. Scanned with globs, every file they match counts as run, as for the files given to node --test.
Supported syntax
Node.js 22.0 and newer, the version the project pins; see framework version.
Where test comes from
| Form | Example |
|---|---|
| Default import | import test from 'node:test', whose test also carries describe, it, suite and the hooks |
| Named import, also renamed | import { describe, it, suite, test } from 'node:test' |
require() | const test = require('node:test'), whole, destructured or in place, as require('node:test').it(...) |
| Kept in a variable | const later = test.skip: only what both branches of a choice share applies, also of one written in place, as (process.env.CI ? test.skip : test)(...) |
| Local helpers file | import { it } from './helpers' or require('./helpers'), followed through local imports, and helper functions that declare tests, read at each call |
node:test sets no globals: it() without an import is not a test.
Calls
| Call | Read as |
|---|---|
test(name, options, fn), it, and describe or suite | A test or describe. Each part may be left out: without a name, the test is named after its function, or <anonymous>; an object first is the options. A test without a function runs and passes. |
.skip, .todo, .only, from Node.js 24.14 .expectFailure | A skipped or todo test or describe, isOnly, or expectedToFail. Any other member, as test.concurrent, is an invalid-chain error: At file level Node.js fails the file; inside a describe it stops that callback, while earlier tests and tests outside it still run. |
{ skip }, { todo } | A skipped or todo test or describe, with a string as its reason; only false and undefined set nothing, as in Node.js, where even '' and 0 skip. A value the scan can't work out is a condition. skip wins over todo, and both over expectFailure. |
{ only } | isOnly. Under node --test, Node.js applies it only with --test-only. |
{ timeout }, { concurrency } | timeout, and isParallel for the tests inside a describe or test whose concurrency is true or above 1. |
{ expectFailure } | From Node.js 24.14 and 25.5, expectedToFail; from 24.15 and 25.8 a string is its label, in reason. |
{ tags } | From Node.js 24.19 and 26.2, tags. |
t.skip(reason), t.todo(reason) | On the test's context, its function's first parameter: the test skipped or todo. The body still runs, and so do its subtests. |
t.test(...), or test() or it() called in a test's body | A subtest: a test record with the titles of the tests around it in suitePath and the test that runs it in parentTestId. A subtest of a test skipped where it is declared is skipped too, as its body never runs. |
before, after, beforeEach, afterEach | Hooks, counted as beforeAll, afterAll, beforeEach and afterEach. |
Not read: t.todo() before a t.test() call, which makes the subtest todo too; t.skip() in a beforeEach hook, which skips each test; t.test() in a helper function the test passes its context to; and a describe inside a test body, which runs as a nested describe and gets a nested-test warning. A glob in a script is read as written, though sh, which npm runs scripts with, reads an unquoted ** as *; quote it, as Node.js expands it then.
The shared syntax rules also apply.
Next
- Shared limits: imports, generated tests, conditions and dependency coverage.
- Key concepts: counts, states, IDs and hashes.
- Output reference and diagnostics: interpret every field and warning.
- CLI, library and MCP: use the same inventory through each interface.
- Compare with node:test: differences from runner collection and execution reports.