Как смоделировать API-клиент, созданный внутри тестового класса? - PullRequest
1 голос
/ 20 мая 2019

У меня есть класс компонента, как показано ниже, который создает остальные соединения и соединения через веб-сокет, используя сторонний модуль npm. Я мог бы изменить Component.constructor так, чтобы он принимал модуль как зависимость, чтобы я мог внедрить фиктивную версию во время моего Jest-тестирования. Но я читал о Mocks с 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?

1 Ответ

2 голосов
/ 20 мая 2019

Вот упрощенный рабочий пример для начала работы:

// @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 файл, содержащий вашу макет.

Другой способ (см. Выше) - использовать jest.mock и передать ему заводскую функцию модуля.

Всякий раз, когда модуль требуется во время теста, Jest возвращает вместо этого проверенный модуль.

Обратите внимание, что приведенный выше пример всегда возвращает один и тот же макет для getSomething и onUpdate, поэтому эти фиктивные функции можно получить во время теста.

Также обратите внимание на использование mockFn.mock.calls для извлечения этой функции стрелки:

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

... который передается onUpdate. После того, как он был извлечен, его можно вызвать напрямую, что вызовет слушателя, как и ожидалось.

...