[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

feat!: clear mocks by default before each test (#10613)

authored by

Vladimir and committed by
GitHub
(Jun 30, 2026, 8:17 AM +0200) 0f6463bf 6815f235

+80 -42
+2 -2
docs/config/clearmocks.md
··· 6 6 # clearMocks 7 7 8 8 - **Type:** `boolean` 9 - - **Default:** `false` 9 + - **Default:** `true` 10 10 11 11 Should Vitest automatically call [`vi.clearAllMocks()`](/api/vi#vi-clearallmocks) before each test. 12 12 ··· 17 17 18 18 export default defineConfig({ 19 19 test: { 20 - clearMocks: true, 20 + clearMocks: false, 21 21 }, 22 22 }) 23 23 ```
+39
docs/guide/migration.md
··· 13 13 Vitest 5.0 is currently in beta. This section tracks breaking changes as they are merged and may change before the stable release. 14 14 ::: 15 15 16 + ### `clearMocks` is Enabled by Default 17 + 18 + [`clearMocks`](/config/#clearmocks) now defaults to `true`. Vitest calls [`vi.clearAllMocks()`](/api/vi#vi-clearallmocks) before every test, resetting the `mock.calls`, `mock.instances`, `mock.contexts` and `mock.results` of every mock. Mock implementations are left intact, so this only affects the recorded history. 19 + 20 + In practice this means a mock no longer carries calls from one test into the next: 21 + 22 + ```ts 23 + import { expect, test, vi } from 'vitest' 24 + 25 + const fn = vi.fn() 26 + 27 + test('first', () => { 28 + fn() 29 + expect(fn).toHaveBeenCalledTimes(1) 30 + }) 31 + 32 + test('second', () => { 33 + fn() 34 + // v4: the call from "first" was kept, so this was 2 // [!code --] 35 + expect(fn).toHaveBeenCalledTimes(2) // [!code --] 36 + // v5: history is cleared before each test, so only this test's call counts // [!code ++] 37 + expect(fn).toHaveBeenCalledTimes(1) // [!code ++] 38 + }) 39 + ``` 40 + 41 + 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. 42 + 43 + To keep the previous behavior, set `clearMocks` back to `false`: 44 + 45 + ```ts [vitest.config.ts] 46 + import { defineConfig } from 'vitest/config' 47 + 48 + export default defineConfig({ 49 + test: { 50 + clearMocks: false, // [!code ++] 51 + }, 52 + }) 53 + ``` 54 + 16 55 ### Benchmarking API Rewrite 17 56 18 57 The benchmarking API has been rewritten. `bench` is no longer a top-level import from `vitest`; it is a [test-context fixture](/guide/test-context#bench) accessed from inside a regular `test()`. See the [Benchmarking guide](/guide/benchmarking) for the new API.
+1 -1
packages/vitest/src/defaults.ts
··· 108 108 watch: !isCI && process.stdin.isTTY && !isAgent, 109 109 globals: false, 110 110 environment: 'node', 111 - clearMocks: false, 111 + clearMocks: true, 112 112 restoreMocks: false, 113 113 mockReset: false, 114 114 unstubGlobals: false,
+1 -1
test/e2e/test/no-module-runner.test.ts
··· 147 147 expect(greet).toHaveBeenCalledOnce() 148 148 }) 149 149 `, 150 - }, { watch: true }) 150 + }, { watch: true, clearMocks: false }) 151 151 152 152 await vitest.waitForStdout('1 passed') 153 153
+1 -1
test/unit/test/mocked-no-mocks.test.ts
··· 16 16 mockedB() 17 17 18 18 // mockedA is not called because mockedB is restored to be undefined 19 - expect(mockedA).toHaveBeenCalledTimes(1) 19 + expect(mockedA).toHaveBeenCalledTimes(0) 20 20 expect(mockedB).toHaveBeenCalledTimes(1) 21 21 })
+30 -28
test/unit/test/test-extend.test.ts
··· 202 202 describe('fixture call times', () => { 203 203 const apiFn = vi.fn(() => true) 204 204 const serviceFn = vi.fn(() => true) 205 - const teardownFn = vi.fn() 205 + let teardownCount = 0 206 206 207 207 interface APIFixture { 208 208 api: boolean ··· 213 213 api: async ({}, use) => { 214 214 await use(apiFn()) 215 215 apiFn.mockClear() 216 - teardownFn() 216 + teardownCount++ 217 217 }, 218 218 service: async ({}, use) => { 219 219 await use(serviceFn()) 220 220 serviceFn.mockClear() 221 - teardownFn() 221 + teardownCount++ 222 222 }, 223 223 }) 224 224 ··· 251 251 afterAll(() => { 252 252 expect(serviceFn).toBeCalledTimes(0) 253 253 expect(apiFn).toBeCalledTimes(0) 254 - expect(teardownFn).toBeCalledTimes(4) 254 + expect(teardownCount).toBe(4) 255 255 }) 256 256 }) 257 257 ··· 261 261 bar: number 262 262 } 263 263 264 - const fooFn = vi.fn(() => 0) 265 - const fooCleanup = vi.fn() 264 + let fooFnCount = 0 265 + let fooCleanupCount = 0 266 266 267 - const barFn = vi.fn(() => 0) 268 - const barCleanup = vi.fn() 267 + let barFnCount = 0 268 + let barCleanupCount = 0 269 269 270 270 const nestedTest = test.extend<Fixture>({ 271 271 async foo({}, use) { 272 - await use(fooFn()) 273 - fooCleanup() 272 + fooFnCount++ 273 + await use(0) 274 + fooCleanupCount++ 274 275 }, 275 276 async bar({}, use) { 276 - await use(barFn()) 277 - barCleanup() 277 + barFnCount++ 278 + await use(0) 279 + barCleanupCount++ 278 280 }, 279 281 }) 280 282 ··· 284 286 285 287 nestedTest('should only initialize foo', ({ foo }) => { 286 288 expect(foo).toBe(0) 287 - expect(fooFn).toBeCalledTimes(1) 288 - expect(barFn).toBeCalledTimes(0) 289 + expect(fooFnCount).toBe(1) 290 + expect(barFnCount).toBe(0) 289 291 }) 290 292 291 293 describe('level 2, using both foo and bar together', () => { ··· 297 299 nestedTest('should initialize foo and bar', ({ foo, bar }) => { 298 300 expect(foo).toBe(0) 299 301 expect(bar).toBe(0) 300 - expect(fooFn).toBeCalledTimes(2) 301 - expect(barFn).toBeCalledTimes(1) 302 + expect(fooFnCount).toBe(2) 303 + expect(barFnCount).toBe(1) 302 304 }) 303 305 304 306 afterEach<Fixture>(({ foo, bar }) => { ··· 307 309 }) 308 310 309 311 afterAll(() => { 310 - expect(barFn).toHaveBeenCalledTimes(1) 311 - expect(barCleanup).toHaveBeenCalledTimes(1) 312 - expect(fooFn).toHaveBeenCalledTimes(2) 313 - expect(barCleanup).toHaveBeenCalledTimes(1) 312 + expect(barFnCount).toBe(1) 313 + expect(barCleanupCount).toBe(1) 314 + expect(fooFnCount).toBe(2) 315 + expect(barCleanupCount).toBe(1) 314 316 }) 315 317 }) 316 318 317 319 nestedTest('should initialize foo again', ({ foo }) => { 318 320 expect(foo).toBe(0) 319 - expect(fooFn).toBeCalledTimes(3) 321 + expect(fooFnCount).toBe(3) 320 322 }) 321 323 322 324 afterEach<Fixture>(({ foo }) => { ··· 324 326 }) 325 327 326 328 afterAll(() => { 327 - expect(fooFn).toHaveBeenCalledTimes(3) 328 - expect(fooCleanup).toHaveBeenCalledTimes(3) 329 - expect(barFn).toHaveBeenCalledTimes(1) 330 - expect(barCleanup).toHaveBeenCalledTimes(1) 329 + expect(fooFnCount).toBe(3) 330 + expect(fooCleanupCount).toBe(3) 331 + expect(barFnCount).toBe(1) 332 + expect(barCleanupCount).toBe(1) 331 333 }) 332 334 }) 333 335 }) 334 336 335 337 // test extend with top level test 336 338 const numbers: number[] = [] 337 - const teardownFn = vi.fn() 339 + let teardownCount = 0 338 340 const teardownTest = test.extend<{ 339 341 numbers: number[] 340 342 }>({ ··· 342 344 numbers.push(1, 2, 3) 343 345 await use(numbers) 344 346 numbers.splice(0, numbers.length) 345 - teardownFn() 347 + teardownCount++ 346 348 }, 347 349 }) 348 350 ··· 352 354 353 355 test('teardown should be called once time', () => { 354 356 expect(numbers).toHaveLength(0) 355 - expect(teardownFn).toBeCalledTimes(1) 357 + expect(teardownCount).toBe(1) 356 358 }) 357 359 358 360 describe('asynchronous setup/teardown', () => {
+4 -4
test/unit/test/vi.spec.ts
··· 263 263 test('can change config', () => { 264 264 const state = getWorkerState() 265 265 expect(state.config.hookTimeout).toBe(10000) 266 - expect(state.config.clearMocks).toBe(false) 267 - vi.setConfig({ hookTimeout: 6000, clearMocks: true }) 268 - expect(state.config.hookTimeout).toBe(6000) 269 266 expect(state.config.clearMocks).toBe(true) 267 + vi.setConfig({ hookTimeout: 6000, clearMocks: false }) 268 + expect(state.config.hookTimeout).toBe(6000) 269 + expect(state.config.clearMocks).toBe(false) 270 270 vi.resetConfig() 271 271 expect(state.config.hookTimeout).toBe(10000) 272 - expect(state.config.clearMocks).toBe(false) 272 + expect(state.config.clearMocks).toBe(true) 273 273 }) 274 274 275 275 test('loads unloaded module', async () => {
+1 -1
packages/vitest/src/node/types/config.ts
··· 496 496 497 497 /** 498 498 * Will call `.mockClear()` on all spies before each test 499 - * @default false 499 + * @default true 500 500 */ 501 501 clearMocks?: boolean 502 502
+1 -4
test/e2e/fixtures/no-module-runner/test/suite.test.ts
··· 1 1 import { assert, describe, expect, it } from 'vitest' 2 - import { getSetupStates, initJsSetup, initTsSetup } from '../src/setups.ts' 2 + import { getSetupStates } from '../src/setups.ts' 3 3 4 4 describe('suite name', () => { 5 5 it('foo', () => { ··· 8 8 9 9 it('setups work', () => { 10 10 // TODO: a separate CLI test that confirms --maxWorkers=1 --no-isolate runs the setup file for every test file 11 - expect(initJsSetup).toHaveBeenCalled() 12 - expect(initTsSetup).toHaveBeenCalled() 13 - 14 11 expect(getSetupStates()).toEqual({ 15 12 jsSetup: true, 16 13 tsSetup: true,