Testing, Debugging and Diagnostics
Write tests that fail when the code is wrong, keep the suite deterministic, and use Node's built-in diagnostics to explain failures in development and production.
Key points
- 1
The built-in
node --testrunner runs each file in its own process by default, runs tests within a file sequentially, and waits for each process to exit, so open handles make it hang. - 2
Use
node:assert/strict: legacyequal/deepEqualcompare loosely.deepStrictEqualuses Object.is for primitives, compares prototypes, and treats Sets and Maps as unordered. - 3
Async code needs
await assert.rejects(...), notassert.throws. An assertion inside an unawaited callback makes the runner report "asynchronous activity after the test ended" and fail the file. - 4
Mock timers fire timer callbacks synchronously, but promise continuations still need a microtask turn; chained sleeps need a tick per step or the async helpers in Jest and Vitest.
- 5
Jest hoists
jest.mockvia Babel for CommonJS; native ESM needsjest.unstable_mockModuleand dynamicimport(). Vitest hoistsvi.mockand offersvi.hoistedfor values the factory needs. - 6
Debug with
--inspect/--inspect-brkon loopback only, and make failures explain themselves with--trace-warnings,--enable-source-maps, diagnostic reports andError.cause. - 7
Use structured logging and AsyncLocalStorage for request-scoped context, and
diagnostics_channelfor observability without monkey-patching.
Common traps
test.onlyis ignored unless you pass--test-only.A timed-out test is counted as cancelled, not failed, but the run still exits with code 1;
todofailures do not fail the run.Binding the inspector to 0.0.0.0 hands remote code execution to anyone who can reach the port.
Test yourself on Testing, Debugging and Diagnostics
Ten questions, with the answer and explanation after each one.