Why mock call counts reset with no code change
This one doesn’t throw an error. There’s no stack trace to search for, which is exactly why it’s confusing to debug: a suite that passed yesterday starts failing today, and nothing in the diff explains why.
Vitest’s own migration guide states the change directly, in the section covering clearMocks’s new default:
clearMocksnow defaults totrue: Vitest callsvi.clearAllMocks()before every test, clearing the recorded history of every mock while leaving implementations intact.
That last clause matters. vi.clearAllMocks() clears each mock’s recorded calls, results, and instances, but it doesn’t touch the mock’s implementation the way vi.resetAllMocks() or vi.restoreAllMocks() would. A mock still returns whatever it was told to return. It just forgets that it was ever called.
The guide also flags where this bites hardest:
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
beforeAllhook) are the most affected, because that history is cleared before the test that asserts on it runs.
A call made once in beforeAll and asserted on later, or a suite that deliberately counts calls cumulatively across a describe block, gets wiped before the assertion that expects to see it.
This changes what 'passing' means for the same test
The guide’s own before/after example makes the shift concrete: a second test
that used to see toHaveBeenCalledTimes(2) (because the first test’s call was
still counted) now sees toHaveBeenCalledTimes(1). Nothing about the mock or
the test code changed. Only the default that decides whether history survives
between tests changed.
Fix it: restore the old default, or stop depending on it
Option 1: set clearMocks back to false
The fastest fix, and the right one if you have a lot of suites written against the old default and no time to audit every one of them right now:
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
// clearMocks not set - Vitest 4 default (false) let call history
// BROKEN under Vitest 5: clearMocks now defaults to true instead
},
});import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
clearMocks: false, // FIXED: restores the old default, call history persists across tests
},
});Option 2: rewrite the test to not depend on cross-test mock state
Vitest’s migration guide shows exactly what a test that relied on the old default looked like, and how it reads once the assumption is made explicit:
import { expect, test, vi } from "vitest";
const fn = vi.fn();
test("first", () => {
fn();
expect(fn).toHaveBeenCalledTimes(1);
});
test("second", () => {
fn();
// v4: the call from "first" was kept, so this was 2
// v5: history is cleared before each test, so only this test's call counts
expect(fn).toHaveBeenCalledTimes(1); // FIXED: asserts only this test's own call
});A test that depends on another test’s mock history to pass is depending on execution order, which is fragile even outside of this specific default change. If you’re touching the suite anyway, this is the fix that actually removes the risk instead of papering over it with a config flag.
Confirmed version range
Documented in Vitest’s own current migration guide as an intentional Vitest 5.0 default-behavior change, in the section covering clearMocks’s new default. The current clearMocks config reference reflects the new default. This repository’s own vitest.config.ts is pinned to vitest@^3.0.0, so this default flip wasn’t independently reproduced against this project’s build; every claim above traces back to Vitest’s migration guide, not a local repro.
This pairs with Fix Vitest 5 vi.mock Top-Level Scope Error, another Vitest 5 mocking change that breaks suites with zero code changes. More fixes like this one are in the Guides & Fixes archive, and every Vitest post is tagged under vitest.







