Skip to content

Commit

Permalink
fix: automock getters and setters (#1903)
Browse files Browse the repository at this point in the history
* fix: automock getters and setters

* refactor: do not mock getters/setters

Co-authored-by: Vladimir Sheremet <sleuths.slews0s@icloud.com>
  • Loading branch information
simon-abbott and sheremet-va committed Aug 30, 2022
1 parent 11ed412 commit 9f887f3
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 5 deletions.
6 changes: 6 additions & 0 deletions examples/mocks/test/automocking.spec.ts
@@ -1,8 +1,10 @@
import type * as exampleModule from '../src/example'
import log from '../src/log'
import { A } from '../src/moduleA'
import { methodSymbol, moduleWithSymbol } from '../src/moduleWithSymbol'

vi.mock('../src/log')
vi.mock('../src/moduleA')
vi.mock('../src/moduleWithSymbol')

test('all mocked are valid', async () => {
Expand Down Expand Up @@ -55,3 +57,7 @@ test('automock properly restores mock', async () => {
expect(moduleWithSymbol[methodSymbol]()).toBe('hello')
expect(moduleWithSymbol.warn()).toBe('hello')
})

test('automock has a getter', () => {
expect(A).toBe('A')
})
12 changes: 8 additions & 4 deletions packages/vitest/src/runtime/mocker.ts
Expand Up @@ -209,10 +209,14 @@ export class VitestMocker {
const isModule = containerType === 'Module' || !!container.__esModule
for (const { key: property, descriptor } of getAllMockableProperties(container)) {
// Modules define their exports as getters. We want to process those.
if (!isModule) {
// TODO: Mock getters/setters somehow?
if (descriptor.get || descriptor.set)
continue
if (!isModule && descriptor.get) {
try {
Object.defineProperty(newContainer, property, descriptor)
}
catch (error) {
// Ignore errors, just move on to the next prop.
}
continue
}

// Skip special read-only props, we don't want to mess with those.
Expand Down
4 changes: 4 additions & 0 deletions test/core/src/mockedC.ts
Expand Up @@ -11,6 +11,10 @@ export class MockedC {
return mockedA()
}

get getOnlyProp(): number {
return 42
}

get getSetProp(): number {
return 123
}
Expand Down
2 changes: 1 addition & 1 deletion test/core/test/mocked.test.js
@@ -1,4 +1,4 @@
// this file should not be converted to js
// this file should not be converted to ts
// so it won't be transformed by esbuild

import { assert, test, vi } from 'vitest'
Expand Down
56 changes: 56 additions & 0 deletions test/core/test/mocked.test.ts
Expand Up @@ -13,6 +13,25 @@ vitest.mock('virtual-module', () => ({ value: 'mock' }))
vitest.mock('../src/mockedC')
vitest.mock('../src/mockedD')

/**
* Get a property descriptor from an object.
*
* This is different from `Object.getOwnPropertyDescriptor` because it recurses
* into the prototype chain until it either finds a match or reaches the end.
*
* @param object The object that contains the property.
* @param property The property.
* @returns The property's descriptor, or undefined if no matching property was found.
*/
function getPropertyDescriptor(object: any, property: PropertyKey) {
for (let o = object; o; o = Object.getPrototypeOf(o)) {
const descriptor = Object.getOwnPropertyDescriptor(o, property)
if (descriptor)
return descriptor
}
return undefined
}

test('submodule is mocked to return "two" as 3', () => {
assert.equal(3, two)
})
Expand Down Expand Up @@ -58,6 +77,43 @@ describe('mocked classes', () => {
expect(MockedC.prototype.doSomething).toHaveBeenCalledOnce()
expect(MockedC.prototype.doSomething).not.toHaveReturnedWith('A')
})

test('should mock getters', () => {
const instance = new MockedC()

expect(instance).toHaveProperty('getOnlyProp')
const descriptor = getPropertyDescriptor(instance, 'getOnlyProp')
expect(descriptor?.get).toBeDefined()
expect(descriptor?.set).not.toBeDefined()

expect(instance.getOnlyProp).toBe(42)
// @ts-expect-error Assign to the read-only prop to ensure it errors.
expect(() => instance.getOnlyProp = 4).toThrow()

const getterSpy = vi.spyOn(instance, 'getOnlyProp', 'get').mockReturnValue(456)
expect(instance.getOnlyProp).toEqual(456)
expect(getterSpy).toHaveBeenCalledOnce()
})

test('should mock getters and setters', () => {
const instance = new MockedC()

expect(instance).toHaveProperty('getSetProp')
const descriptor = getPropertyDescriptor(instance, 'getSetProp')
expect(descriptor?.get).toBeDefined()
expect(descriptor?.set).toBeDefined()

expect(instance.getSetProp).toBe(123)
expect(() => instance.getSetProp = 4).not.toThrow()

const getterSpy = vi.spyOn(instance, 'getSetProp', 'get').mockReturnValue(789)
expect(instance.getSetProp).toEqual(789)
expect(getterSpy).toHaveBeenCalledOnce()

const setterSpy = vi.spyOn(instance, 'getSetProp', 'set')
instance.getSetProp = 159
expect(setterSpy).toHaveBeenCalledWith(159)
})
})

describe('default exported classes', () => {
Expand Down

0 comments on commit 9f887f3

Please sign in to comment.