[英]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.