it-roy-ru.com

Как изменить макетную реализацию для каждого отдельного теста [Jestjs]

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

Короче, вот чего я пытаюсь достичь:

  1. mock зависимость
  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 eeffects
  });

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

Я попробовал несколько стратегий, но я не нашел никакого решения, которое мог бы определить удовлетворяющее.


1 - mockFn.mockImplementationOnce (fn)

pros

  • Возвращается к исходной реализации после первого вызова

cons

  • Он сломается, если тест вызовет b больше раз
  • Он не вернется к исходной реализации , Пока не будет вызвана b (утечка в тесте next)

code:

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 (moduleName, factory, options)

pros

  • Явно перепроверяется на каждом тесте

cons

  • Не удается определить макетную реализацию по умолчанию для всех тестов
  • Невозможно Расширить реализацию по умолчанию, заставляя повторно объявлять каждый ложный метод

code:

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 - Ручной макет с использованием методов установки (как объяснено здесь )

pros

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

cons

  • Лот шаблонного кода
  • Трудно поддерживать в долгосрочной перспективе

code:

__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 (object, methodName)

cons

  • Я не могу вернуть обратно mockImplementation к исходному ложному возвращаемому значению, поэтому влияет на следующие тесты

code:

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 the original mocked value?
});

Заранее спасибо за любой вклад/предложение!

18
Andrea Carraro

Хорошим шаблоном для написания теста является создание функции фабрики настроек, которая возвращает данные, необходимые для тестирования текущего модуля.

Ниже приведен пример кода, следующий за вашим вторым примером, хотя он позволяет предоставлять значения по умолчанию и переопределять значения повторно.

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)
  })
});
6
user1095118

Немного опоздал на вечеринку, но если у кого-то еще возникли проблемы с этим.

Мы используем TypeScript, ES6 и babel для быстрой разработки.

Мы обычно высмеиваем внешние модули NPM в корневом каталоге __mocks__.

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

    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

2
Thomas Hagström

Используйте mockFn.mockImplementation (fn) .

Поместите реализацию по умолчанию в beforeEach. Макет будет сброшен на это перед каждым тестом.

Чтобы переопределить, используйте mockImplementation в тесте.

Это переопределит поведение макета для любых/всех вызовов в тесте и будет перезаписано реализацией beforeEach перед следующим тестом.

Например:

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 */ });
  // ...
});
0
A Jar of Clay