[英]How to spy on a class constructor jest?
export class Foo {
public static bar() {
doSomething();
}
constructor(paramA, paramB) {
}
}
对于 class 中的方法,我们可以使用jest.spyOn(Foo, 'bar')
来监视该方法。 构造函数怎么样? 我们如何监视 object 是如何实例化的?
@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);
});
如果你真的想监视构造函数,你可以这样做:
// 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);
});
我认为没有官方的方法。 这是我个人的做法:
const spy = jest.fn()
function Mock (...args) {
spy(...args)
Constructor.apply(this, args)
}
Mock.prototype = Constructor.prototype
然后我可以检查间谍:
expect(spy).toHaveBeenCalledWith('firstArg', 'secondArg')
不幸的是,你不能直接窥探construtor。 问题在于新的操作符是一种语言特征,因此很难为了测试目的而分离。
要测试你应该这样做:
export class Foo {
public static bar() {
new Foo();
}
constructor(paramA, paramB) {
}
}
然后:
jest.spyOn(Foo, 'bar').something(() => ());
// 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)
例如,如果您需要验证传递给构造函数的参数,则不必直接模拟 ctor'。
我在 ctor 的类中创建了一个静态方法,例如
static checkData(data) {/*some validation on data*/}
并用jest.spy.mockImplementation
正常jest.spy.mockImplementation
它,对数据参数进行一些验证。
然后在ctor'中,我用我想要的任何输入调用这个checkData
函数,特别是ctor'的参数。
和 walla,ctor 的 args 在不嘲笑 ctor 本身的情况下得到验证。
我创建了一个助手 function 来为我的多个班级做这个:
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;
}
之后,我为每个 class 执行以下操作,我想为其构造函数进行监视
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');
});
我明白你的意思,我花了一些时间寻找解决方案。 我最终创建了这个代码片段,它可以监视整个 class,包括它的构造函数。 而且用法也很简单,您可以将此代码段添加到文件中并在需要时导入它。
这是代码(打字稿/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();
});
希望它可以帮助你和其他人。
其实有办法:)
它甚至在官方文档中: 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);
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.