Short answer
In Vitest 1.0.0, globalSetup is designed to run once per test run — before any test files are collected — and globalTeardown runs once after everything finishes. It does not run per test file, even under --watch. If you are seeing it execute repeatedly, something else is almost certainly triggering it.
Why it can look like per-file execution
Three common explanations account for the reports of duplicated setup:
- Multiple workers. When Vitest runs tests across worker threads or processes, each worker can execute
globalSetup once. With several workers this looks like repeated execution, even though it is once per worker per run, not per file.
- Confusion with
setupFiles. The legacy setupFiles entry does run before every test file. If both are configured, expensive logic placed in a setup file will repeat per file while globalSetup runs once.
- Watch-mode restarts. In watch mode the runner persists between file changes, so editing an unrelated test file should not re-run
globalSetup. But editing the config, the setup file itself, or anything else that triggers a full restart will run it again. That is a new run, not per-file behavior.
How to verify on your project
Add a timestamp and PID log at the top of your setup file, then compare runs:
// global.setup.ts
export default function setup() {
console.log(`[globalSetup] pid=${process.pid} at=${Date.now()}`);
}
Run vitest --run, then vitest --watch and edit an unrelated test file. If the log appears once per worker on startup and stays silent on file edits, behavior is as designed. If it re-fires on every edit, check whether the edited file is in the config's watch/restart scope, and confirm setupFiles is not also configured. Repeat with a single worker (--pool=forks --poolOptions.forks.singleFork or equivalent for your version) to separate per-worker from per-run execution.
Avoiding redundant setup
Because globalSetup runs in a separate context from test files, do not assume singletons initialized there are shared with tests. Keep it for truly global resources (databases, containers, seed data) and make it idempotent — e.g., check whether the resource already exists before creating it — so a watch restart or multi-worker run is cheap. Per-file or per-suite initialization belongs in setupFiles or beforeAll, not in globalSetup.
Interactions with the other 1.0.0 changes
- Snapshots: The serialization update in 1.0.0 can make snapshots written with v0.28 incompatible. Regenerate with
vitest -u after upgrading and review the diff. This is orthogonal to globalSetup timing — one does not cause the other.
- Mocks: Automatic mock restoration after each test means mocks no longer persist across tests or files by default. If your suite relied on a mock surviving across files, that pattern breaks regardless of
globalSetup; establish such mocks explicitly per suite, since globalSetup state is not shared with test workers anyway.
Caveats
Watch-mode restart logic changed across later 1.x and 2.x releases, so this analysis is specific to the 1.0.0 line — verify against your exact version with the logging check above. If your logs show globalSetup firing per file with a single worker and no config edits, that would be worth reporting as a bug with a minimal reproduction.