Як змінити макетну реалізацію на основі кожного тесту [Jestjs]


86

Я хотів би змінити реалізацію знущаної залежності на кожну тестову основу , розширивши поведінку макета за замовчуванням і повернувши її назад до початкової реалізації при виконанні наступного тесту.

Коротше це те, чого я намагаюся досягти:

  1. фіктивна залежність
  2. змінити / розширити фіктивну реалізацію в одному тесті
  3. повернутися до початкової макети при виконанні наступного тесту

В даний час я використовую Jest v21.

Ось як би виглядав типовий тест Jest:

__mocks__/myModule.js

const myMockedModule = jest.genMockFromModule('../myModule');

myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);

export default myMockedModule;

__tests__/myTest.js

import myMockedModule from '../myModule';

// Mock myModule
jest.mock('../myModule');

beforeEach(() => {
  jest.clearAllMocks();
});

describe('MyTest', () => {
  it('should test with default mock', () => {
    myMockedModule.a(); // === true
    myMockedModule.b(); // === true
  });

  it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
    // Extend change mock
    myMockedModule.a(); // === true
    myMockedModule.b(); // === 'overridden'
    // Restore mock to original implementation with no side effects
  });

  it('should revert back to default myMockedModule mock', () => {
    myMockedModule.a(); // === true
    myMockedModule.b(); // === true
  });
});

Ось те, що я намагався до цього часу:


1 - mockFn.mockImplementationOnce (fn)

плюси

  • Повертається до початкової реалізації після першого дзвінка

мінуси

  • Він розривається, якщо тест викликає bкілька разів
  • Він не повертається до початкової реалізації, поки bне буде викликаний (витік у наступному тесті)

код:

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  myMockedModule.b.mockImplementationOnce(() => 'overridden');

  myModule.a(); // === true
  myModule.b(); // === 'overridden'
});

2 - jest.doMock (ім'я модуля, фабрика, параметри)

плюси

  • Явно повторно знущається над кожним тестом

мінуси

  • Не вдається визначити макетну реалізацію за замовчуванням для всіх тестів
  • Неможливо розширити реалізацію за замовчуванням, що змушує повторно оголошувати кожен знущаний метод

код:

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  jest.doMock('../myModule', () => {
    return {
      a: jest.fn(() => true,
      b: jest.fn(() => 'overridden',
    }
  });

  myModule.a(); // === true
  myModule.b(); // === 'overridden'
});

3 - Знущання вручну методами сетера (як пояснено тут )

плюси

  • Повний контроль над знущаними результатами

мінуси

  • Лот типового коду
  • Важко підтримувати довгостроково

код:

__mocks__/myModule.js

const myMockedModule = jest.genMockFromModule('../myModule');

let a = true;
let b = true;

myMockedModule.a = jest.fn(() => a);
myMockedModule.b = jest.fn(() => b);

myMockedModule.__setA = (value) => { a = value };
myMockedModule.__setB = (value) => { b = value };
myMockedModule.__reset = () => {
  a = true;
  b = true;
};
export default myMockedModule;

__tests__/myTest.js

it('should override myModule.b mock result (and leave the other methods untouched)', () => {
  myModule.__setB('overridden');

  myModule.a(); // === true
  myModule.b(); // === 'overridden'

  myModule.__reset();
});

4 - jest.spyOn (об'єкт, ім'я методу)

мінуси

  • Я не можу повернутися mockImplementationдо початкового знущаного значення повернення, що впливає на наступні тести

код:

beforeEach(() => {
  jest.clearAllMocks();
  jest.restoreAllMocks();
});

// Mock myModule
jest.mock('../myModule');

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden');

  myMockedModule.a(); // === true
  myMockedModule.b(); // === 'overridden'

  // How to get back to original mocked value?
});

Приємно. Але як зробити варіант 2 для модуля npm, такого як '@ private-repo / module'? Більшість прикладів, які я бачу, мають відносні шляхи? Це працює також для встановлених модулів?
mrbinky3000

Відповіді:


47

Гарним шаблоном для написання тесту є створення заводської функції установки, яка повертає дані, необхідні для тестування поточного модуля.

Нижче наведено деякий зразок коду за вашим другим прикладом, хоча дозволяє надавати значення за замовчуванням і перевизначати значення багаторазовим способом.

const spyReturns = returnValue => jest.fn(() => returnValue);

describe("scenario", () => {
  const setup = (mockOverrides) => {
    const mockedFunctions =  {
      a: spyReturns(true),
      b: spyReturns(true),
      ...mockOverrides
    }
    return {
      mockedModule: jest.doMock('../myModule', () => mockedFunctions)
    }
  }

  it("should return true for module a", () => {
    const { mockedModule } = setup();
    expect(mockedModule.a()).toEqual(true)
  });

  it("should return override for module a", () => {
    const EXPECTED_VALUE = "override"
    const { mockedModule } = setup({ a: spyReturns(EXPECTED_VALUE)});
    expect(mockedModule.a()).toEqual(EXPECTED_VALUE)
  });
});

40

Ваніль JS

Використовуйте mockFn.mockImplementation (fn) .

import { funcToMock } from './somewhere';
jest.mock('./somewhere');

beforeEach(() => {
  funcToMock.mockImplementation(() => { /* default implementation */ });
});

test('case that needs a different implementation of funcToMock', () => {
  funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
  // ...
});

TypeScript

Щоб запобігти повідомленню mockImplementation не є властивістю funcToMock , вам потрібно буде вказати тип, наприклад, змінивши верхній рядок зверху на такий:

import { (funcToMock as jest.Mock) } from './somewhere';

Питання, що стосується цієї проблеми, можна знайти тут: властивість jest typecript макет не існує для типу


21

Трохи пізно на вечірку, але якщо хтось інший має проблеми з цим.

Ми використовуємо TypeScript, ES6 та Babel для реагування на рідну розробку.

Зазвичай ми висміюємо зовнішні модулі NPM у кореневій __mocks__директорії.

Я хотів замінити певну функцію модуля в класі Auth aws-amplify для конкретного тесту.

    import { Auth } from 'aws-amplify';
    import GetJwtToken from './GetJwtToken';
    ...
    it('When idToken should return "123"', async () => {
      const spy = jest.spyOn(Auth, 'currentSession').mockImplementation(() => ({
        getIdToken: () => ({
          getJwtToken: () => '123',
        }),
      }));

      const result = await GetJwtToken();
      expect(result).toBe('123');
      spy.mockRestore();
    });

Суть: https://gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2

Підручник: https://medium.com/p/b4ac52a005d#19c5

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.