Skip to content

Commit 0f6463b

Browse files
authored
feat!: clear mocks by default before each test (#10613)
1 parent 6815f23 commit 0f6463b

9 files changed

Lines changed: 80 additions & 42 deletions

File tree

docs/config/clearmocks.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ outline: deep
66
# clearMocks
77

88
- **Type:** `boolean`
9-
- **Default:** `false`
9+
- **Default:** `true`
1010

1111
Should Vitest automatically call [`vi.clearAllMocks()`](/api/vi#vi-clearallmocks) before each test.
1212

@@ -17,7 +17,7 @@ import { defineConfig } from 'vitest/config'
1717

1818
export default defineConfig({
1919
test: {
20-
clearMocks: true,
20+
clearMocks: false,
2121
},
2222
})
2323
```

docs/guide/migration.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,45 @@ outline: deep
1313
Vitest 5.0 is currently in beta. This section tracks breaking changes as they are merged and may change before the stable release.
1414
:::
1515

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+
1655
### Benchmarking API Rewrite
1756

1857
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.

packages/vitest/src/defaults.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export const configDefaults: Readonly<{
108108
watch: !isCI && process.stdin.isTTY && !isAgent,
109109
globals: false,
110110
environment: 'node',
111-
clearMocks: false,
111+
clearMocks: true,
112112
restoreMocks: false,
113113
mockReset: false,
114114
unstubGlobals: false,

packages/vitest/src/node/types/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ export interface InlineConfig {
496496

497497
/**
498498
* Will call `.mockClear()` on all spies before each test
499-
* @default false
499+
* @default true
500500
*/
501501
clearMocks?: boolean
502502

test/e2e/fixtures/no-module-runner/test/suite.test.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { assert, describe, expect, it } from 'vitest'
2-
import { getSetupStates, initJsSetup, initTsSetup } from '../src/setups.ts'
2+
import { getSetupStates } from '../src/setups.ts'
33

44
describe('suite name', () => {
55
it('foo', () => {
@@ -8,9 +8,6 @@ describe('suite name', () => {
88

99
it('setups work', () => {
1010
// 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-
1411
expect(getSetupStates()).toEqual({
1512
jsSetup: true,
1613
tsSetup: true,

test/e2e/test/no-module-runner.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ describe.runIf(module.registerHooks)('supported', () => {
147147
expect(greet).toHaveBeenCalledOnce()
148148
})
149149
`,
150-
}, { watch: true })
150+
}, { watch: true, clearMocks: false })
151151

152152
await vitest.waitForStdout('1 passed')
153153

test/unit/test/mocked-no-mocks.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ test('mocking several modules work', () => {
1616
mockedB()
1717

1818
// mockedA is not called because mockedB is restored to be undefined
19-
expect(mockedA).toHaveBeenCalledTimes(1)
19+
expect(mockedA).toHaveBeenCalledTimes(0)
2020
expect(mockedB).toHaveBeenCalledTimes(1)
2121
})

test/unit/test/test-extend.test.ts

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ describe('test.extend()', () => {
202202
describe('fixture call times', () => {
203203
const apiFn = vi.fn(() => true)
204204
const serviceFn = vi.fn(() => true)
205-
const teardownFn = vi.fn()
205+
let teardownCount = 0
206206

207207
interface APIFixture {
208208
api: boolean
@@ -213,12 +213,12 @@ describe('test.extend()', () => {
213213
api: async ({}, use) => {
214214
await use(apiFn())
215215
apiFn.mockClear()
216-
teardownFn()
216+
teardownCount++
217217
},
218218
service: async ({}, use) => {
219219
await use(serviceFn())
220220
serviceFn.mockClear()
221-
teardownFn()
221+
teardownCount++
222222
},
223223
})
224224

@@ -251,7 +251,7 @@ describe('test.extend()', () => {
251251
afterAll(() => {
252252
expect(serviceFn).toBeCalledTimes(0)
253253
expect(apiFn).toBeCalledTimes(0)
254-
expect(teardownFn).toBeCalledTimes(4)
254+
expect(teardownCount).toBe(4)
255255
})
256256
})
257257

@@ -261,20 +261,22 @@ describe('test.extend()', () => {
261261
bar: number
262262
}
263263

264-
const fooFn = vi.fn(() => 0)
265-
const fooCleanup = vi.fn()
264+
let fooFnCount = 0
265+
let fooCleanupCount = 0
266266

267-
const barFn = vi.fn(() => 0)
268-
const barCleanup = vi.fn()
267+
let barFnCount = 0
268+
let barCleanupCount = 0
269269

270270
const nestedTest = test.extend<Fixture>({
271271
async foo({}, use) {
272-
await use(fooFn())
273-
fooCleanup()
272+
fooFnCount++
273+
await use(0)
274+
fooCleanupCount++
274275
},
275276
async bar({}, use) {
276-
await use(barFn())
277-
barCleanup()
277+
barFnCount++
278+
await use(0)
279+
barCleanupCount++
278280
},
279281
})
280282

@@ -284,8 +286,8 @@ describe('test.extend()', () => {
284286

285287
nestedTest('should only initialize foo', ({ foo }) => {
286288
expect(foo).toBe(0)
287-
expect(fooFn).toBeCalledTimes(1)
288-
expect(barFn).toBeCalledTimes(0)
289+
expect(fooFnCount).toBe(1)
290+
expect(barFnCount).toBe(0)
289291
})
290292

291293
describe('level 2, using both foo and bar together', () => {
@@ -297,8 +299,8 @@ describe('test.extend()', () => {
297299
nestedTest('should initialize foo and bar', ({ foo, bar }) => {
298300
expect(foo).toBe(0)
299301
expect(bar).toBe(0)
300-
expect(fooFn).toBeCalledTimes(2)
301-
expect(barFn).toBeCalledTimes(1)
302+
expect(fooFnCount).toBe(2)
303+
expect(barFnCount).toBe(1)
302304
})
303305

304306
afterEach<Fixture>(({ foo, bar }) => {
@@ -307,42 +309,42 @@ describe('test.extend()', () => {
307309
})
308310

309311
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)
314316
})
315317
})
316318

317319
nestedTest('should initialize foo again', ({ foo }) => {
318320
expect(foo).toBe(0)
319-
expect(fooFn).toBeCalledTimes(3)
321+
expect(fooFnCount).toBe(3)
320322
})
321323

322324
afterEach<Fixture>(({ foo }) => {
323325
expect(foo).toBe(0)
324326
})
325327

326328
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)
331333
})
332334
})
333335
})
334336

335337
// test extend with top level test
336338
const numbers: number[] = []
337-
const teardownFn = vi.fn()
339+
let teardownCount = 0
338340
const teardownTest = test.extend<{
339341
numbers: number[]
340342
}>({
341343
numbers: async ({}, use) => {
342344
numbers.push(1, 2, 3)
343345
await use(numbers)
344346
numbers.splice(0, numbers.length)
345-
teardownFn()
347+
teardownCount++
346348
},
347349
})
348350

@@ -352,7 +354,7 @@ teardownTest('test without describe', ({ numbers }) => {
352354

353355
test('teardown should be called once time', () => {
354356
expect(numbers).toHaveLength(0)
355-
expect(teardownFn).toBeCalledTimes(1)
357+
expect(teardownCount).toBe(1)
356358
})
357359

358360
describe('asynchronous setup/teardown', () => {

test/unit/test/vi.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,13 +263,13 @@ describe('testing vi utils', () => {
263263
test('can change config', () => {
264264
const state = getWorkerState()
265265
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)
269266
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)
270270
vi.resetConfig()
271271
expect(state.config.hookTimeout).toBe(10000)
272-
expect(state.config.clearMocks).toBe(false)
272+
expect(state.config.clearMocks).toBe(true)
273273
})
274274

275275
test('loads unloaded module', async () => {

0 commit comments

Comments
 (0)