Skip to content

Commit db7a888

Browse files
authoredDec 5, 2024··
feat!: spy.mockReset changes (#6426)
1 parent a11d565 commit db7a888

File tree

8 files changed

+115
-28
lines changed

8 files changed

+115
-28
lines changed
 

‎docs/api/mock.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,13 @@ await asyncMock() // throws Error<'Async error'>
197197
function mockReset(): MockInstance<T>
198198
```
199199

200-
Performs the same actions as `mockClear` and sets the inner implementation to an empty function (returning `undefined` when invoked). This also resets all "once" implementations. It is useful for completely resetting a mock to its default state.
200+
Does what `mockClear` does and resets inner implementation to the original function.
201+
This also resets all "once" implementations.
202+
203+
Note that resetting a mock from `vi.fn()` will set implementation to an empty function that returns `undefined`.
204+
resetting a mock from `vi.fn(impl)` will restore implementation to `impl`.
205+
206+
This is useful when you want to reset a mock to its original state.
201207

202208
To automatically call this method before each test, enable the [`mockReset`](/config/#mockreset) setting in the configuration.
203209

@@ -207,9 +213,10 @@ To automatically call this method before each test, enable the [`mockReset`](/co
207213
function mockRestore(): MockInstance<T>
208214
```
209215

210-
Performs the same actions as `mockReset` and restores the inner implementation to the original function.
216+
Does what `mockReset` does and restores original descriptors of spied-on objects.
211217

212-
Note that restoring a mock created with `vi.fn()` will set the implementation to an empty function that returns `undefined`. Restoring a mock created with `vi.fn(impl)` will restore the implementation to `impl`.
218+
Note that restoring a mock from `vi.fn()` will set implementation to an empty function that returns `undefined`.
219+
Restoring a mock from `vi.fn(impl)` will restore implementation to `impl`.
213220

214221
To automatically call this method before each test, enable the [`restoreMocks`](/config/#restoremocks) setting in the configuration.
215222

‎docs/api/vi.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -398,15 +398,18 @@ Checks that a given parameter is a mock function. If you are using TypeScript, i
398398

399399
### vi.clearAllMocks
400400

401-
Will call [`.mockClear()`](/api/mock#mockclear) on all spies. This will clear mock history, but not reset its implementation to the default one.
401+
Calls [`.mockClear()`](/api/mock#mockclear) on all spies.
402+
This will clear mock history without affecting mock implementations.
402403

403404
### vi.resetAllMocks
404405

405-
Will call [`.mockReset()`](/api/mock#mockreset) on all spies. This will clear mock history and reset its implementation to an empty function (will return `undefined`).
406+
Calls [`.mockReset()`](/api/mock#mockreset) on all spies.
407+
This will clear mock history and reset each mock's implementation to its original.
406408

407409
### vi.restoreAllMocks
408410

409-
Will call [`.mockRestore()`](/api/mock#mockrestore) on all spies. This will clear mock history and reset its implementation to the original one.
411+
Calls [`.mockRestore()`](/api/mock#mockrestore) on all spies.
412+
This will clear mock history, restore all original mock implementations, , and restore original descriptors of spied-on objects.
410413

411414
### vi.spyOn
412415

‎docs/config/index.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -1953,21 +1953,24 @@ Custom [commands](/guide/browser/commands) that can be imported during browser t
19531953
- **Type:** `boolean`
19541954
- **Default:** `false`
19551955

1956-
Will call [`.mockClear()`](/api/mock#mockclear) on all spies before each test. This will clear mock history, but not reset its implementation to the default one.
1956+
Will call [`.mockClear()`](/api/mock#mockclear) on all spies before each test.
1957+
This will clear mock history without affecting mock implementations.
19571958

19581959
### mockReset
19591960

19601961
- **Type:** `boolean`
19611962
- **Default:** `false`
19621963

1963-
Will call [`.mockReset()`](/api/mock#mockreset) on all spies before each test. This will clear mock history and reset its implementation to an empty function (will return `undefined`).
1964+
Will call [`.mockReset()`](/api/mock#mockreset) on all spies before each test.
1965+
This will clear mock history and reset each implementation to its original.
19641966

19651967
### restoreMocks
19661968

19671969
- **Type:** `boolean`
19681970
- **Default:** `false`
19691971

1970-
Will call [`.mockRestore()`](/api/mock#mockrestore) on all spies before each test. This will clear mock history and reset its implementation to the original one.
1972+
Will call [`.mockRestore()`](/api/mock#mockrestore) on all spies before each test.
1973+
This will clear mock history, restore each implementation to its original, and restore original descriptors of spied-on objects..
19711974

19721975
### unstubEnvs {#unstubenvs}
19731976

‎docs/guide/migration.md

+8
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,14 @@ Jest has their [globals API](https://jestjs.io/docs/api) enabled by default. Vit
297297

298298
If you decide to keep globals disabled, be aware that common libraries like [`testing-library`](https://testing-library.com/) will not run auto DOM [cleanup](https://testing-library.com/docs/svelte-testing-library/api/#cleanup).
299299

300+
### `spy.mockReset`
301+
302+
Jest's [`mockReset`](https://jestjs.io/docs/mock-function-api#mockfnmockreset) replaces the mock implementation with an
303+
empty function that returns `undefined`.
304+
305+
Vitest's [`mockReset`](/api/mock#mockreset) resets the mock implementation to its original.
306+
That is, resetting a mock created by `vi.fn(impl)` will reset the mock implementation to `impl`.
307+
300308
### Module Mocks
301309

302310
When mocking a module in Jest, the factory argument's return value is the default export. In Vitest, the factory argument has to return an object with each export explicitly defined. For example, the following `jest.mock` would have to be updated as follows:

‎packages/mocker/src/automocker.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,9 @@ export function mockObject(
120120
const original = this[key]
121121
const mock = spyOn(this, key as string)
122122
.mockImplementation(original)
123-
mock.mockRestore = () => {
124-
mock.mockReset()
123+
const origMockReset = mock.mockReset
124+
mock.mockRestore = mock.mockReset = () => {
125+
origMockReset.call(mock)
125126
mock.mockImplementation(original)
126127
return mock
127128
}
@@ -132,8 +133,9 @@ export function mockObject(
132133
const mock = spyOn(newContainer, property)
133134
if (options.type === 'automock') {
134135
mock.mockImplementation(mockFunction)
135-
mock.mockRestore = () => {
136-
mock.mockReset()
136+
const origMockReset = mock.mockReset
137+
mock.mockRestore = mock.mockReset = () => {
138+
origMockReset.call(mock)
137139
mock.mockImplementation(mockFunction)
138140
return mock
139141
}

‎packages/spy/src/index.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,17 @@ export interface MockInstance<T extends Procedure = Procedure> {
198198
*/
199199
mockClear(): this
200200
/**
201-
* Performs the same actions as `mockClear` and sets the inner implementation to an empty function (returning `undefined` when invoked). This also resets all "once" implementations. It is useful for completely resetting a mock to its default state.
201+
* Does what `mockClear` does and resets inner implementation to the original function. This also resets all "once" implementations.
202+
*
203+
* Note that resetting a mock from `vi.fn()` will set implementation to an empty function that returns `undefined`.
204+
* Resetting a mock from `vi.fn(impl)` will set implementation to `impl`. It is useful for completely resetting a mock to its default state.
202205
*
203206
* To automatically call this method before each test, enable the [`mockReset`](https://vitest.dev/config/#mockreset) setting in the configuration.
204207
* @see https://vitest.dev/api/mock#mockreset
205208
*/
206209
mockReset(): this
207210
/**
208-
* Does what `mockReset` does and restores inner implementation to the original function.
211+
* Does what `mockReset` does and restores original descriptors of spied-on objects.
209212
*
210213
* Note that restoring mock from `vi.fn()` will set implementation to an empty function that returns `undefined`. Restoring a `vi.fn(impl)` will restore implementation to `impl`.
211214
* @see https://vitest.dev/api/mock#mockrestore
@@ -536,15 +539,14 @@ function enhanceSpy<T extends Procedure>(
536539

537540
stub.mockReset = () => {
538541
stub.mockClear()
539-
implementation = (() => undefined) as T
542+
implementation = undefined
540543
onceImplementations = []
541544
return stub
542545
}
543546

544547
stub.mockRestore = () => {
545548
stub.mockReset()
546549
state.restore()
547-
implementation = undefined
548550
return stub
549551
}
550552

‎packages/vitest/src/integrations/vi.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -313,21 +313,29 @@ export interface VitestUtils {
313313
isMockFunction: (fn: any) => fn is MockInstance
314314

315315
/**
316-
* Calls [`.mockClear()`](https://vitest.dev/api/mock#mockclear) on every mocked function. This will only empty `.mock` state, it will not reset implementation.
316+
* Calls [`.mockClear()`](https://vitest.dev/api/mock#mockclear) on every mocked function.
317317
*
318-
* It is useful if you need to clean up mock between different assertions.
318+
* This will only empty `.mock` state, it will not affect mock implementations.
319+
*
320+
* This is useful if you need to clean up mocks between different assertions within a test.
319321
*/
320322
clearAllMocks: () => VitestUtils
321323

322324
/**
323-
* Calls [`.mockReset()`](https://vitest.dev/api/mock#mockreset) on every mocked function. This will empty `.mock` state, reset "once" implementations and force the base implementation to return `undefined` when invoked.
325+
* Calls [`.mockReset()`](https://vitest.dev/api/mock#mockreset) on every mocked function.
326+
*
327+
* This will empty `.mock` state, reset "once" implementations, and reset each mock's base implementation to its original.
324328
*
325-
* This is useful when you want to completely reset a mock to the default state.
329+
* This is useful when you want to reset all mocks to their original states.
326330
*/
327331
resetAllMocks: () => VitestUtils
328332

329333
/**
330-
* Calls [`.mockRestore()`](https://vitest.dev/api/mock#mockrestore) on every mocked function. This will restore all original implementations.
334+
* Calls [`.mockRestore()`](https://vitest.dev/api/mock#mockrestore) on every mocked function.
335+
*
336+
* This will empty `.mock` state, restore all original mock implementations, and restore original descriptors of spied-on objects.
337+
*
338+
* This is useful for inter-test cleanup and/or removing mocks created by [`vi.spyOn(...)`](https://vitest.dev/api/vi#vi-spyon).
331339
*/
332340
restoreAllMocks: () => VitestUtils
333341

‎test/core/test/jest-mock.test.ts

+60-6
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ describe('jest mock compat layer', () => {
218218
expect(a.mock.invocationCallOrder[0]).toBeLessThan(b.mock.invocationCallOrder[0])
219219
})
220220

221-
it('getter spyOn', () => {
221+
it('should spy on property getter, and mockRestore should restore original descriptor', () => {
222222
const obj = {
223223
get getter() {
224224
return 'original'
@@ -244,9 +244,39 @@ describe('jest mock compat layer', () => {
244244
spy.mockRestore()
245245

246246
expect(obj.getter).toBe('original')
247+
expect(spy).not.toHaveBeenCalled()
248+
})
249+
250+
it('should spy on property getter, and mockReset should not restore original descriptor', () => {
251+
const obj = {
252+
get getter() {
253+
return 'original'
254+
},
255+
}
256+
257+
const spy = vi.spyOn(obj, 'getter', 'get')
258+
259+
expect(obj.getter).toBe('original')
260+
261+
spy.mockImplementation(() => 'mocked').mockImplementationOnce(() => 'once')
262+
263+
expect(obj.getter).toBe('once')
264+
expect(obj.getter).toBe('mocked')
265+
expect(obj.getter).toBe('mocked')
266+
267+
spy.mockReturnValue('returned').mockReturnValueOnce('returned-once')
268+
269+
expect(obj.getter).toBe('returned-once')
270+
expect(obj.getter).toBe('returned')
271+
expect(obj.getter).toBe('returned')
272+
273+
spy.mockReset()
274+
275+
expect(obj.getter).toBe('original')
276+
expect(spy).toHaveBeenCalled()
247277
})
248278

249-
it('getter function spyOn', () => {
279+
it('should spy on function returned from property getter', () => {
250280
const obj = {
251281
get getter() {
252282
return function () {
@@ -266,7 +296,7 @@ describe('jest mock compat layer', () => {
266296
expect(obj.getter()).toBe('mocked')
267297
})
268298

269-
it('setter spyOn', () => {
299+
it('should spy on property setter (1)', () => {
270300
let setValue = 'original'
271301
let mockedValue = 'none'
272302

@@ -309,7 +339,7 @@ describe('jest mock compat layer', () => {
309339
expect(setValue).toBe('last')
310340
})
311341

312-
it('should work - setter', () => {
342+
it('should spy on property setter (2), and mockRestore should restore original descriptor', () => {
313343
const obj = {
314344
_property: false,
315345
set property(value) {
@@ -327,12 +357,36 @@ describe('jest mock compat layer', () => {
327357
obj.property = false
328358
spy.mockRestore()
329359
obj.property = true
330-
// unlike jest, mockRestore only restores implementation to the original one,
331-
// we are still spying on the setter
360+
// like jest, mockRestore restores the original descriptor,
361+
// we are not spying on the setter any more
332362
expect(spy).not.toHaveBeenCalled()
333363
expect(obj.property).toBe(true)
334364
})
335365

366+
it('should spy on property setter (2), and mockReset should not restore original descriptor', () => {
367+
const obj = {
368+
_property: false,
369+
set property(value) {
370+
this._property = value
371+
},
372+
get property() {
373+
return this._property
374+
},
375+
}
376+
377+
const spy = vi.spyOn(obj, 'property', 'set')
378+
obj.property = true
379+
expect(spy).toHaveBeenCalled()
380+
expect(obj.property).toBe(true)
381+
obj.property = false
382+
spy.mockReset()
383+
obj.property = true
384+
// unlike jest, vitest's mockReset will restore original implementation without restoring the original descriptor.
385+
// We are still spying on the setter
386+
expect(spy).toHaveBeenCalled()
387+
expect(obj.property).toBe(true)
388+
})
389+
336390
it('throwing', async () => {
337391
const fn = vi.fn(() => {
338392
// eslint-disable-next-line no-throw-literal

0 commit comments

Comments
 (0)
Please sign in to comment.