Study notes · 7% of the exam

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. 1

    The built-in node --test runner 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. 2

    Use node:assert/strict: legacy equal/deepEqual compare loosely. deepStrictEqual uses Object.is for primitives, compares prototypes, and treats Sets and Maps as unordered.

  3. 3

    Async code needs await assert.rejects(...), not assert.throws. An assertion inside an unawaited callback makes the runner report "asynchronous activity after the test ended" and fail the file.

  4. 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. 5

    Jest hoists jest.mock via Babel for CommonJS; native ESM needs jest.unstable_mockModule and dynamic import(). Vitest hoists vi.mock and offers vi.hoisted for values the factory needs.

  6. 6

    Debug with --inspect/--inspect-brk on loopback only, and make failures explain themselves with --trace-warnings, --enable-source-maps, diagnostic reports and Error.cause.

  7. 7

    Use structured logging and AsyncLocalStorage for request-scoped context, and diagnostics_channel for observability without monkey-patching.

Common traps

  • test.only is ignored unless you pass --test-only.

  • A timed-out test is counted as cancelled, not failed, but the run still exits with code 1; todo failures 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.