Skip to content

Commit f71004f

Browse files
authored
fix(spy): clear/reset/restore mocks in stack order (#7499)
1 parent 1d9a6f4 commit f71004f

File tree

3 files changed

+27
-31
lines changed

3 files changed

+27
-31
lines changed

packages/spy/src/index.ts

-25
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,6 @@ export type Mocked<T> = {
412412
: T[P];
413413
} & T
414414

415-
const vitestSpy = Symbol.for('vitest.spy')
416415
export const mocks: Set<MockInstance> = new Set()
417416

418417
export function isMockFunction(fn: any): fn is MockInstance {
@@ -421,20 +420,6 @@ export function isMockFunction(fn: any): fn is MockInstance {
421420
)
422421
}
423422

424-
function getSpy(
425-
obj: unknown,
426-
method: keyof any,
427-
accessType?: 'get' | 'set',
428-
): MockInstance | undefined {
429-
const desc = Object.getOwnPropertyDescriptor(obj, method)
430-
if (desc) {
431-
const fn = desc[accessType ?? 'value']
432-
if (typeof fn === 'function' && vitestSpy in fn) {
433-
return fn
434-
}
435-
}
436-
}
437-
438423
export function spyOn<T, S extends Properties<Required<T>>>(
439424
obj: T,
440425
methodName: S,
@@ -464,11 +449,6 @@ export function spyOn<T, K extends keyof T>(
464449
} as const
465450
const objMethod = accessType ? { [dictionary[accessType]]: method } : method
466451

467-
const currentStub = getSpy(obj, method, accessType)
468-
if (currentStub) {
469-
return currentStub
470-
}
471-
472452
const stub = tinyspy.internalSpyOn(obj, objMethod as any)
473453

474454
return enhanceSpy(stub) as MockInstance
@@ -542,11 +522,6 @@ function enhanceSpy<T extends Procedure>(
542522

543523
let name: string = (stub as any).name
544524

545-
Object.defineProperty(stub, vitestSpy, {
546-
value: true,
547-
enumerable: false,
548-
})
549-
550525
stub.getMockName = () => name || 'vi.fn()'
551526
stub.mockName = (n) => {
552527
name = n

packages/vitest/src/integrations/vi.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -616,17 +616,17 @@ function createVitest(): VitestUtils {
616616
},
617617

618618
clearAllMocks() {
619-
mocks.forEach(spy => spy.mockClear())
619+
[...mocks].reverse().forEach(spy => spy.mockClear())
620620
return utils
621621
},
622622

623623
resetAllMocks() {
624-
mocks.forEach(spy => spy.mockReset())
624+
[...mocks].reverse().forEach(spy => spy.mockReset())
625625
return utils
626626
},
627627

628628
restoreAllMocks() {
629-
mocks.forEach(spy => spy.mockRestore())
629+
[...mocks].reverse().forEach(spy => spy.mockRestore())
630630
return utils
631631
},
632632

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

+24-3
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ describe('jest mock compat layer', () => {
363363
expect(obj.property).toBe(true)
364364
})
365365

366-
it('spyOn returns the same spy twice', () => {
366+
it('spyOn multiple times', () => {
367367
const obj = {
368368
method() {
369369
return 'original'
@@ -375,19 +375,40 @@ describe('jest mock compat layer', () => {
375375

376376
expect(vi.isMockFunction(obj.method)).toBe(true)
377377
expect(obj.method()).toBe('mocked')
378-
expect(spy1).toBe(spy2)
378+
expect(spy1).not.toBe(spy2)
379379

380380
spy2.mockImplementation(() => 'mocked2')
381381

382382
expect(obj.method()).toBe('mocked2')
383383

384384
spy2.mockRestore()
385385

386-
expect(obj.method()).toBe('original')
386+
expect(obj.method()).toBe('mocked')
387+
expect(vi.isMockFunction(obj.method)).toBe(true)
388+
expect(obj.method).toBe(spy1)
389+
390+
spy1.mockRestore()
387391
expect(vi.isMockFunction(obj.method)).toBe(false)
388392
expect(obj.method).not.toBe(spy1)
389393
})
390394

395+
it('restoreAllMocks in stack order', () => {
396+
const obj = { foo: () => 'foo' }
397+
398+
vi.spyOn(obj, 'foo').mockImplementation(() => 'mocked1')
399+
expect(obj.foo()).toBe('mocked1')
400+
expect(vi.isMockFunction(obj.foo)).toBe(true)
401+
402+
vi.spyOn(obj, 'foo').mockImplementation(() => 'mocked2')
403+
expect(obj.foo()).toBe('mocked2')
404+
expect(vi.isMockFunction(obj.foo)).toBe(true)
405+
406+
vi.restoreAllMocks()
407+
408+
expect(obj.foo()).toBe('foo')
409+
expect(vi.isMockFunction(obj.foo)).toBe(false)
410+
})
411+
391412
it('should spy on property setter (2), and mockReset should not restore original descriptor', () => {
392413
const obj = {
393414
_property: false,

0 commit comments

Comments
 (0)