Skip to content

feat: add a testIsolation build option to run each test file in its own process - #514

Merged
dsherret merged 4 commits into
denoland:mainfrom
dsherret:fix_test_file_isolation
Jul 27, 2026
Merged

feat: add a testIsolation build option to run each test file in its own process#514
dsherret merged 4 commits into
denoland:mainfrom
dsherret:fix_test_file_isolation

Conversation

@dsherret

@dsherret dsherret commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Closes #432

The test runner loads every test file into the same process, so a test file's module state leaks into the next one. With @std/testing/bdd the second file's global hooks hit TestSuiteInternal.started:

Error: Cannot add global hooks after a global test is registered

...or, when the first file already created the global suite, the hooks silently attach to the already drained global test so those tests never run:

Running tests in ./script/b.test.js...

Running tests in ./esm/b.test.js...

deno test gives each test file its own isolate. Since doing the equivalent means starting a process per test file, it's opt in:

await build({
  // ...etc...
  testIsolation: "process",
});

The generated runner then re-spawns itself once per test file. The children inherit stdio so the output is the same, the node arguments of the parent are forwarded, and the parent exits non-zero when any file fails. A preload module is loaded before each test file in this mode, which is what the isolation implies.

The default ("none") keeps the current behavior of running everything in one process, so nothing changes for anyone who doesn't opt in.

Added tests/test_hooks_project, where the second test file adds a global hook. Loading both of its output files in one process errors with the message above, and the fixture passes with testIsolation: "process".

dsherret added 4 commits July 26, 2026 23:57
A test file's module state leaked into the next one because they were
all loaded into the same process. With `@std/testing/bdd` this meant
the global hooks of the second file either errored with "Cannot add
global hooks after a global test is registered" or silently registered
tests that never ran.

`deno test` gives each test file its own isolate, so do the equivalent
by spawning a process per file.

Closes denoland#432
- make the fixture actually fail without the fix by having the second
  test file be the one that adds a global hook
- forward the node arguments of the parent process to each child
- report a spawn failure and an unknown file index
- update the docs for the preload module running before each test file
Running each test file in its own process starts a process per test
file, so make it opt in with `testIsolation: "process"` and keep
running them all in the same process by default.
@dsherret dsherret changed the title fix: run each test file in its own process feat: add a testIsolation build option to run each test file in its own process Jul 27, 2026
@dsherret
dsherret merged commit edbcc92 into denoland:main Jul 27, 2026
5 checks passed
@dsherret
dsherret deleted the fix_test_file_isolation branch July 27, 2026 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

global hooks with @std/testing show "Error: Cannot add global hooks after a global test is registered"

1 participant