簡體   English   中英

如何監視 class 構造函數的笑話?

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

參考How to Use Jest to Mock Constructors

例如,如果您需要驗證傳遞給構造函數的參數,則不必直接模擬 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM