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

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

  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;

__ тесты __ / 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;

__ тесты __ / 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?
});

Ответы (4)

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

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


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

описать ("сценарий", () => {
  beforeEach (() => {
    jest.resetModules ();
  });

  const setup = (mockOverrides) => {
    const mockedFunctions = {
      a: spyReturns (правда),
      b: spyReturns (правда),
      ... mockOverrides
    }
    jest.doMock ('../ myModule', () => mockedFunctions)
    возвращение {
      mockedModule: require ('../ myModule')
    }
  }

  it ("должен вернуть истину для модуля a", () => {
    const {mockedModule} = настройка ();
    ожидать (mockedModule.a ()). toEqual (правда)
  });

  it ("должен вернуть переопределение для модуля a", () => {
    const EXPECTED_VALUE = "переопределить"
    const {mockedModule} = настройка ({a: spyReturns (EXPECTED_VALUE)});
    ожидать (mockedModule.a ()). toEqual (EXPECTED_VALUE)
  });
});

Важно сказать, что вы должны сбросить модули, которые были кэшированы с помощью jest.resetModules (). Это можно сделать с помощью beforeEach или аналогичной функции удаления.

Для получения дополнительной информации см. Документацию по объекту jest: https://jestjs.io/docs/jest-object.

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

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

Обычно мы имитируем внешние модули NPM в корневом каталоге __ mocks __.

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

 импорт {Auth} из 'aws-ampify';
    импортировать GetJwtToken из './GetJwtToken';
    ...
    it ('Когда idToken должен вернуть "123"', async () => {
      const spy = jest.spyOn (Auth, 'currentSession'). mockImplementation (() => ({
        getIdToken: () => ({
          getJwtToken: () => '123',
        }),
      }));

      const результат = ждать GetJwtToken ();
      ожидать (результат) .toBe ('123');
      spy.mockRestore ();
    });

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

Учебник: https://medium.com/p/b4ac52a005d#19c5

When mocking a single method (when it's required to leave the rest of a class/module implementation intact) I discovered the following approach to be helpful to reset any implementation tweaks from individual tests.

Я нашел этот подход наиболее лаконичным, без необходимости jest.mock чего-то в начале файла и т. Д. Вам нужен только код, который вы видите ниже, чтобы имитировать MyClass. methodName. Еще одним преимуществом является то, что по умолчанию spyOn сохраняет исходную реализацию метода, но также сохраняет всю статистику (количество вызовов, аргументов, результатов и т. Д.) Для проверки, а сохранение реализации по умолчанию является обязательным в некоторых случаях. . Таким образом, у вас есть возможность сохранить реализацию по умолчанию или изменить ее простым добавлением .mockImplementation, как указано в приведенном ниже коде.

Код находится на TypeScript с комментариями, подчеркивающими разницу для JS (если быть точным, разница в одной строке). Протестировано с Jest 26.6.

describe('test set', () => {
    let mockedFn: jest.SpyInstance; // void is the return value of the mocked function, change as necessary
    // For plain JS use just: let mockedFn;

    beforeEach(() => {
        mockedFn = jest.spyOn(MyClass.prototype, 'methodName');
        // Use the following instead if you need not to just spy but also to replace the default method implementation:
        // mockedFn = jest.spyOn(MyClass.prototype, 'methodName').mockImplementation(() => {/*custom implementation*/});
    });

    afterEach(() => {
        // Reset to the original method implementation (non-mocked) and clear all the mock data
        mockedFn.mockRestore();
    });

    it('does first thing', () => {
        /* Test with the default mock implementation */
    });

    it('does second thing', () => {
        mockedFn.mockImplementation(() => {/*custom implementation just for this test*/});
        /* Test utilising this custom mock implementation. It is reset after the test. */
    });

    it('does third thing', () => {
        /* Another test with the default mock implementation */
    });
});

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

импорт {funcToMock} из './somewhere';
jest.mock ('./ где-нибудь');

beforeEach (() => {
  funcToMock.mockImplementation (() => {/ * реализация по умолчанию * /});
  // (funcToMock как jest.Mock) ... в TS
});

test ('случай, который требует другой реализации funcToMock', () => {
  funcToMock.mockImplementation (() => {/ * реализация, специфичная для этого теста * /});
  // (funcToMock как jest.Mock) ... в TS

  // ...
});

2022 WebDevInsider