如何监视类构造函数jest?

rsl1atfo  于 2022-12-08  发布在  Jest
关注(0)|答案(8)|浏览(196)
export class Foo {
    public static bar() {
        doSomething();
    }

    constructor(paramA, paramB) {

    } 
}

对于一个类中的一个方法,我们可以使用jest.spyOn(Foo, 'bar')来窥探这个方法。那么构造函数呢?我们如何窥探对象是如何示例化的呢?

368yc8dk

368yc8dk1#

@gillyb是对的,只是忘了“模拟”Foo模块

// Foo.js
export class Foo {
    public static bar() {
        doSomething();
    }

    constructor(paramA, paramB) {

    } 
}

// Foo.spec.js
import Foo from './Foo.js';
jest.mock('./Foo.js');

it('test something...', () => {
    // Assuming we did something that called our constructor
    expect(Foo).toHaveBeenCalledTimes(1);
});
4ngedf3f

4ngedf3f2#

如果你真的想监视构造函数,你可以这样做:

// MyClass.js

export class MyClass {
  constructor() {
    console.log("constructing");
  }
}

// MyClass.test.js

import * as MyClassModule from './MyClass';

const MyClass = MyClassModule.MyClass;

test('the constructor is called', () => {
  const constructorSpy = jest.spyOn(MyClassModule, 'MyClass');
  new MyClass();
  expect(constructorSpy).toHaveBeenCalledTimes(1);
});
jei2mxaa

jei2mxaa3#

我觉得没有官方的办法,我个人是这样做的:

const spy = jest.fn()
function Mock (...args) {
  spy(...args)
  Constructor.apply(this, args)
}
Mock.prototype = Constructor.prototype

那我就查一下间谍:

expect(spy).toHaveBeenCalledWith('firstArg', 'secondArg')
2exbekwf

2exbekwf4#

// Actual Implementation 
import { ModuleToMock} from 'module-to-mock'

const test = new ModuleToMock('value1', 'value2')

test.setUser({ name: 'test', address: 'myTest' })

// test case using jest 
 import { ModuleToMock} from 'module-to-mock'
 jest.mock('module-to-mock')

// constructor response 
   const mockedModuleResponse = {
   setUser: jest.fn(), 
   }

ModuleToMock.mockImplementation(() => mockedModuleResponse)

来自How to Use Jest to Mock Constructors的引用

efzxgjgh

efzxgjgh5#

其实还有一个办法:)
甚至在官方文件中也有记载:https://jestjs.io/docs/en/es6-class-mocks#complete-example
下面是您可以如何使用代码执行此操作:

// Foo.js
export class Foo {
    public static bar() {
        doSomething();
    }

    constructor(paramA, paramB) {

    } 
}

// Foo.spec.js
import Foo from './Foo.js';

it('test something...', () => {
    // Assuming we did something that called our constructor
    expect(Foo).toHaveBeenCalledTimes(1);
});
w8biq8rn

w8biq8rn6#

例如,如果您需要验证传递给构造函数的参数,则不必直接模拟ctor'。
我在ctor的类中创建了一个静态方法。

static checkData(data) {/*some validation on data*/}

jest.spy.mockImplementation进行正常的模拟,对数据参数进行一些验证。
在ctor'中,我调用这个checkData函数,输入我想要的任何输入,特别是ctor'的参数。
而且walla,ctor的参数是在不模仿ctor本身的情况下验证的。

50few1ms

50few1ms7#

我创建了一个helper函数来为我的多个类执行此操作:

export function MockConstructorSpy(module: string, className: string, isDefault: boolean) {
  const spyMethod = jest.fn();
  jest.mock(module, () => {
    let mockClass = null;
    if (isDefault) {
      mockClass = jest.requireActual(module).default;
    } else {
      const {[className]: mockedModuleClass} = jest.requireActual(module);
      mockClass = mockedModuleClass;
    }
    class Mock extends mockClass {
      constructor(...args: any) {
        super(...args);
        spyMethod(...args);
      }
    }
    if (isDefault) return Mock;
    else return {[className]: Mock};
  });
  return spyMethod;
}

然后,我对每个要探查其构造函数的类执行以下操作

const {MockConstructorSpy} = require('../mock-constructor-spy');
const spyCredentialCreation = MockConstructorSpy('./credential-request', 'CredentialRequest', false);
// The import must be done after the call to MockConstructorSpy
import {CredentialRequest} from './credential-request';

it('should catch the constructor call', () => {
  const a = new CredentialRequest('a', 'b');
  expect(spyCredentialCreation).toHaveBeenCalled();
  expect(a.email).toBe('a');
});
camsedfj

camsedfj8#

我理解你的观点,我花了一些时间来寻找解决方案。我最终创建了这个代码片段,它可以监视整个类,包括它的构造函数。而且用法也很简单,你可以把这个片段添加到一个文件中,然后在需要的时候导入它。
下面是代码(typescript/ES6):

/**
 * spyOn references to classes. Use it with spyOnClass
 */
export const classSpy: any = {};

/**
 * Utility to Spy On all class methods. Not including the constructor
 * @returns a spyOn references to all the class methods
 * includes the methods mockClear and mockReset as convenience
 * to trigger the respective method for all the spies
 */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function spyOnClassMethods(proto: any): any {
    const properties = Object.getOwnPropertyNames(proto);
    const spyHolder: any = {};
    for (const i in properties) { spyHolder[properties[i]] = jest.spyOn(proto, properties[i]); }
    spyHolder.mockClear = (): void => { for (const i in properties) { spyHolder[properties[i]].mockClear(); } };
    spyHolder.mockReset = (): void => { for (const i in properties) { spyHolder[properties[i]].mockReset(); } };
    return spyHolder;
}
// To attend jest.mock problems, the should start with 'mock'
const mocksSpyOnClassMethods = spyOnClassMethods;

/**
 * Utility to Spy On all class methods and its constructor.
 */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function spyOnClass(mockModuleName: string, mockClassName: string): any {
    classSpy[mockClassName] = {};
    jest.mock(mockModuleName, () => {
        const module = jest.requireActual(mockModuleName) as any;
        const mock = {};
        classSpy[mockClassName] = mocksSpyOnClassMethods(module[mockClassName].prototype);
        mock[mockClassName] = jest.fn().mockImplementation(
            (...args: any[]) => {
                const instance = new module[mockClassName](...args);
                classSpy[mockClassName].constructor = mock[mockClassName];
                return instance;
            }
        );
        return { ...module, ...mock };
    });
}

用法示例:

import { classSpy, spyOnClass } from './mock-utils';

// If you import ClassName, this must come before the import.
spyOnClass('module-name', 'ClassName');

import { ClassName } from 'module-name';

test('this', () => {
    doSomethingThatUsesClassName();
    expect(classSpy.ClassName.constructor).toHaveBeenCalled();
    expect(classSpy.ClassName.someMethod).toHaveBeenCalled();
});

希望它能对你和其他人有所帮助。

相关问题