【问题标题】:How to mock API client created inside a test class?如何模拟在测试类中创建的 API 客户端?
【发布时间】:2019-10-06 10:14:24
【问题描述】:

我有一个组件类,如下所示,它使用第三方 npm 模块创建 rest 和 websocket 连接。我可以更改 Component.constructor 以接受模块作为依赖项,以便在我的 Jest 测试期间注入模拟版本。但是我读到了 Mocks with Jest,我想我想尝试一下,但我似乎无法理解如何拦截 Api.Rest() 和 Api.Websocket 返回值。

// component.ts
import * as Api from 'npm-module'
import * as wait from 'wait-for-stuff' // actual npm module
export class Component {
    private _rest:any;
    private _websocket:any;
    public events = new EventEmitter();
    constructor() {
        // I want to intecept the return value of
        // Api.Rest() and Api.Websocket() to use mock versions.
        this._rest = new Api.Rest();
        this._websocket = new Api.Websocket();

        this._init();
    }

    private _init() {
        // so that when do stuff with this._rest and this._websocket;
        // I can control what is the expected results during test
        this._websocket.onUpdate((data) => {
            events.emit('update', data);
        });
        var value = wait.for.promise(this._rest.getSomething());
    }
}

我是否必须使用其他测试库,例如 Sinon 或 Jasmine?

【问题讨论】:

    标签: node.js jasmine jestjs sinon


    【解决方案1】:

    这是一个简化的工作示例,可帮助您入门:

    // @ts-ignore
    import * as Api from 'npm-module';  // <= (ts-ignore since "npm-module" doesn't exist)
    import EventEmitter from 'events';
    
    jest.mock('npm-module', () => {
      const getSomethingMock = jest.fn();  // <= always return...
      const onUpdateMock = jest.fn();  // <= ...the same mocks...
      return {
        Rest: () => ({ getSomething: getSomethingMock }),
        Websocket: () => ({ onUpdate: onUpdateMock })
      }
    },
    { virtual: true });  // <= (use virtual since "npm-module" doesn't exist)
    
    class Component {
      private _rest: any;
      private _websocket: any;
      public events = new EventEmitter();
      constructor() {
        this._rest = new Api.Rest();
        this._websocket = new Api.Websocket();
        this._init();
      }
    
      private _init() {
        this._websocket.onUpdate((data) => {  // <= ...so that this onUpdate...
          this.events.emit('update', data);
        });
      }
    }
    
    test('Component', () => {
      const component = new Component();
      const listener = jest.fn();
      component.events.on('update', listener);
      const onUpdate = new Api.Websocket().onUpdate;  // <= ...is the same as this one
      const onUpdateArrowFunction = onUpdate.mock.calls[0][0];  // <= get the arrow function passed to it
      onUpdateArrowFunction('mock data');  // <= now call the function
      expect(listener).toHaveBeenCalledWith('mock data');  // Success!
    });
    

    详情

    Jest 接管require 系统并允许您指定在需要模块时希望它返回的内容(请注意,TypeScript 的import 语句被编译为require 调用)。

    模拟模块的一种方法是通过在 __mocks__/npm-module.ts 创建一个包含您的模拟的文件来创建一个 manual mock

    另一种方法(如上所示)是使用jest.mock 并将其传递给模块工厂函数。

    只要在测试期间需要模块,Jest 就会返回模拟模块。

    请注意,上面的示例始终为 getSomethingonUpdate 返回相同的模拟,因此可以在测试期间检索这些模拟函数。

    还要注意使用mockFn.mock.calls 来检索这个箭头函数:

    (data) => {
      this.events.emit('update', data);
    }
    

    ...它被传递给onUpdate。一旦它被检索到,就可以直接调用它,按预期触发监听器。

    【讨论】:

    • 只是为了报告,我在组件类中得到TypeError: Api.Rest is not a constructor 的这一行this._rest = new Api.Rest()。所以它对我不起作用,我仍在拼命尝试。
    猜你喜欢
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-03
    • 1970-01-01
    相关资源
    最近更新 更多