Skip to content

Commit

Permalink
fix: delegate prototype mock call
Browse files Browse the repository at this point in the history
  • Loading branch information
hi-ogawa committed Nov 21, 2023
1 parent fa9e458 commit 5d5d0c1
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 10 deletions.
10 changes: 7 additions & 3 deletions packages/vitest/src/runtime/mocker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,16 +328,20 @@ export class VitestMocker {
const spyModule = this.spyModule
if (!spyModule)
throw this.createError('[vitest] `spyModule` is not defined. This is Vitest error. Please open a new issue with reproduction.')
const mock = spyModule.spyOn(newContainer, property).mockImplementation(function (this: any) {
// do similar to ject? https://github.com/jestjs/jest/blob/2c3d2409879952157433de215ae0eee5188a4384/packages/jest-mock/src/index.ts#L678-L691
const mock = spyModule.spyOn(newContainer, property).mockImplementation(function (this: any, ...args: any[]) {
// jest reference
// https://github.com/jestjs/jest/blob/2c3d2409879952157433de215ae0eee5188a4384/packages/jest-mock/src/index.ts#L678-L691

// check constructor call
if (this instanceof newContainer[property]) {
// mock each class instance's method
// TODO: more sound way to loop prorotype methods?
for (const key in this) {
if (typeof this[key] === 'function') {
// TODO: ability to restore?
spyModule.spyOn(this, key).mockImplementation(() => undefined)
// mock but delegate to original prototype method, which should be also mocked already
const original = this[key]
spyModule.spyOn(this, key).mockImplementation(() => original.apply(this, args))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions test/core/src/mockedE.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export class MockedE {
public doSomething() {
return 'hello'
public doSomething(arg: string) {
return arg.repeat(2)
}
}
17 changes: 14 additions & 3 deletions test/core/test/mocked-wip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { MockedE } from '../src/mockedE'

vi.mock('../src/mockedE')

// TODO: move to mocked.test.ts
// TODO: move to mocked.test.ts?

test('mock each instance method separately', () => {
expect(MockedE).toBeTypeOf('function')
Expand All @@ -16,8 +16,19 @@ test('mock each instance method separately', () => {
expect(instance1.doSomething).not.toBe(MockedE.prototype.doSomething)
expect(vi.mocked(instance1.doSomething).mock).not.toBe(vi.mocked(instance2.doSomething).mock)

instance1.doSomething()
// TODO: check input/output
instance1.doSomething('a')
expect(instance1.doSomething).toBeCalledTimes(1)
expect(instance2.doSomething).toBeCalledTimes(0)
expect(MockedE.prototype.doSomething).toBeCalledTimes(0)
expect(MockedE.prototype.doSomething).toBeCalledTimes(1)

instance2.doSomething('b')
expect(instance1.doSomething).toBeCalledTimes(1)
expect(instance2.doSomething).toBeCalledTimes(1)
expect(MockedE.prototype.doSomething).toBeCalledTimes(2)

instance1.doSomething('c')
expect(instance1.doSomething).toBeCalledTimes(2)
expect(instance2.doSomething).toBeCalledTimes(1)
expect(MockedE.prototype.doSomething).toBeCalledTimes(3)
})
3 changes: 1 addition & 2 deletions test/core/test/mocked.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ describe('mocked classes', () => {
expect(instance.doSomething).toBeTypeOf('function')
expect(instance.doSomething()).not.toBe('A')

// TODO: check what jest does
expect(MockedC.prototype.doSomething).not.toHaveBeenCalledOnce()
expect(MockedC.prototype.doSomething).toHaveBeenCalledOnce()
expect(MockedC.prototype.doSomething).not.toHaveReturnedWith('A')
})

Expand Down

0 comments on commit 5d5d0c1

Please sign in to comment.