Architecture

This document explains how jest-doctor integrates with Jest and enforces test isolation.

Non-goals

High-level design

jest-doctor works by augmenting the Jest test environment, not by modifying test code.

Core ideas:

  1. Each test owns its async resources
  2. Upon test end, async resources must fully clean
  3. Any leftover resource is a hard failure

This design prioritizes deterministic failures over permissive behavior.

Integration points

jest-doctor provides custom environments:

These environments:

Execution lifecycle

For each test:

  1. Before test
    • Initialize leak records
    • Patch globals (timers, console, test functions)
    • Start async_hooks tracking
  2. During test
    • Attribute async resources to current test
    • Capture creation stack traces
  3. After test
    • Detect leftover async resources
    • Report leaks
    • Cleanup globals and hooks

Leak Detection Internals

This section describes how jest-doctor detects leaks.

Leak categories

jest-doctor currently detects:

Category Detection mechanism
Promises async_hooks
Timers Global API patching
Fake timers Jest fake timer patching
Console output Console method patching
Process output process method patching
DOM listeners (add/remove)-EventListener patching

Promise detection

Real timers

Global functions are patched timers.ts:

Records:

The legacy fake timer global useRealTimers function is also patched to restore patches once applied.

Fake timers

Used when Jest fake timers are enabled fakeTimers.ts:

Console detection

Console methods are patched console.ts

Console output is treated as a leak.

Records:

Rationale:

Treating console output as a leak is a deliberate strictness choice. This enforces explicit assertions and prevents silent failures in CI. The react example shows a common problem that can be caught by tests that mock console correctly.

Process outputs

process.(stderr/stdout).write are patched processOutput.ts:

DOM Listeners

window.(add/remove)EventListener are patched domListeners.ts

Attached DOM listeners after a test are treated as a leak.

Records:

Ownership attribution

All resources are associated using:

This ensures:

Cleanup

jest-doctor will clear open timers to avoid cascading failures and ensure test isolation. Clean up happens after each test and will not interfere with reporting.

But it will still throw or warn based on configuration. Warnings and errors never suppress cleanup or attribution.

cleanupAfterTest.ts

Error reporting

Leaks are reported with:

A single error is thrown per test for clarity.

All reports are summed up and sent to the reporter at the end of each test file.