Build1 distinct publisher3 min readPublished
The new default runs vi.clearAllMocks() ahead of each test, so calls recorded in a setup file or a beforeAll hook are gone before the assertion looks for them, and the floor moves to Vite 6.4 and Node 22.12.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
The pattern that breaks is record once, assert many. Vitest's migration guide names the places where calls get recorded outside a test body: a setup file, the top level of a module, and a beforeAll hook [5]. The clear runs before each test, so when the assertion executes, the mock's ledger holds only the calls that test made itself [3]. The gap between the old expectation and the new count is exactly what was recorded before the test started [18]. The guide's example shows the smallest version of that: an assertion that had to read toHaveBeenCalledTimes(2) on Vitest 4 reads 1 on 5 [4]. Implementations are untouched by the clear, so a vi.fn() with a factory still returns what it returned [3].
The change I would grep for before bumping is the test name filter. testNamePattern, and the -t flag behind it, now matches the suite chain and test name joined by ' > ', which is the string the reporter already prints [7]. Only patterns that cross a segment boundary change meaning, so -t 'math adds' has to become -t 'math > adds', or -t adds, or -t 'math.*adds' [8][9]. Joining on the reporter's separator is the better default, because the line you copy out of a failure is now a valid filter. The exposure sits elsewhere: the guide's testNamePattern section does not say what a run whose pattern matches nothing does [10].
On the config side, extends now defaults to true for inline projects, so each one inherits the root config, Vite plugins and resolve.alias included [11]. Arrays merge rather than override, which means a project that defines setupFiles appends them to the inherited ones [12]. Config files and directories referenced from test.projects still inherit nothing [13], but a referenced config that declares projects of its own now hands back those nested projects, named app (unit) and so on, where Vitest 4 ignored the field [15]. An option that did nothing for a major version is an option somebody left in a file. That one at least fails at startup, with a missing-file error, a no-projects-found error, or a circular definition [16].
Order the migration so each failure has one cause:
1. Move Node and Vite first, on the Vitest you already run; the guide puts the prerequisites ahead of every other step [2]. 2. Bump Vitest with clearMocks: false and extends: false already in the config [6][14], and get the suite green under the old semantics. 3. Remove clearMocks: false, then fix the tests that assert on calls made before them. 4. Remove extends: false one project at a time, checking each project's setup files for duplicates. 5. Search the repo and the CI config for -t patterns and for stray projects fields in referenced configs.
None of that is extra work. It is the same work, arranged so a red test tells you which default caused it.
Ranked by verification strength, evidence, and original report placement.
Vitest 5.0 requires Vite >= 6.4.0 and Node.js >= 22.12.0.
The migration guide instructs users to ensure the environment meets the Vite and Node requirements before proceeding with any other migration steps, and says running Vitest 5.0 on older versions of Vite or Node.js is not supported and may result in unexpected errors.
clearMocks now defaults to true: Vitest calls vi.clearAllMocks() before every test, clearing the recorded history of every mock while leaving implementations intact.
In the guide's example, a second test calls the same mock once and asserts on the count: under Vitest 4 the call from the first test was kept so the assertion read 2, and under Vitest 5 history is cleared before each test so it reads 1.
Tests that record calls outside of the test body, for example in a setup file, at the top level of a module, or in a beforeAll hook, are the most affected, because that history is cleared before the test that asserts on it runs.
Setting test.clearMocks to false in the Vitest config keeps the previous behaviour.
Distinct publishers with included, body-backed reporting in this cluster.
1 article · September 4, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
FFmpeg.wasm ships, but budget for a watchdog: one in ten heavy jobs deadlocked in silence1 distinct publisher
build
An open-ended security override handed vitest a Vite major it cannot run1 distinct publisher
build
Build concurrency on one VPS is a division problem, and the app you serve pays the remainder1 distinct publisher
build
Bundle archaeology beats "make it faster": 2.1 MB to 890 KB, one change per measurement1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
First-party, precise, uncorroborated
Every detail traces to Vitest's own migration guide, and it is unusually specific for release documentation: config snippets for both opt-outs, the assertion that read 2 and now reads 1, the literal startup error strings a stray projects field produces. That is the authoritative account of what changed. It is also the only account in this reporting, and the guide leaves one gap it could have closed, saying nothing about a -t pattern that now matches no tests.
No uptake signal in view
The guide's publication is the only dated event here, and documentation of a release says nothing about how many suites have upgraded or how many hit the cleared-history failures. Scoring uptake from a migration guide would be inventing the number.
Understated where it fails quietly
The tone is calmer than the change warrants in one specific place. Cleared mock history announces itself as a failed assertion on the first run, so it gets found. A -t filter joined with a single space is still legal input that now selects nothing, and the section introducing that change never says what a zero-match run does - a job that ran no tests can look a lot like a job that passed.
Author is the upgrade's vendor
The project that shipped these defaults is also the one explaining them, to readers it wants to keep moving forward. That shows in the shape of the page rather than in any suppression: the two changes with a switch get copy-paste snippets, the two without get advice and a caution. Nobody outside the project is checking the framing.
Solid on behaviour, blind on cost
For what the release does, documentation from the implementer is close to the best available source, and the Vite 6.4 and Node 22.12 requirements can be verified in an afternoon. The uncertainty is scope rather than accuracy: with one source and no upgrade experience to compare against, how much a real suite pays for the cleared history or the rewritten filters is unknown here.