Fix Vitest 5 clearMocks Breaking Mock State

vitest.config.ts diff showing clearMocks changing from its old false default to Vitest 5's new true default
On this page

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:

clearMocks now defaults to true: Vitest calls vi.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 beforeAll hook) 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:

vitest.config.ts - before (relies on the implicit Vitest 4 default)
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
  },
});
vitest.config.ts - after (explicit, matches Vitest 4 behavior)
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:

mock-history.test.ts - Vitest's own before/after example
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.

Frequently asked

Does this also change resetMocks or restoreMocks defaults?

Vitest's migration guide documents this default flip only for clearMocks. It doesn't mention resetMocks or restoreMocks changing their own defaults, so treat those as unaffected until you check the current guide yourself rather than assuming they moved too.

Is this a bug I should report, or intentional?

Intentional. Vitest's own migration guide lists it as a deliberate Vitest 5.0 default-behavior change, not a regression to report upstream.

Emitted as FAQPage JSON-LD from the same frontmatter — one source, no duplicated prose.

Recent posts

Full-text search via Pagefind · ↑↓ to navigate · ↵ to open