Макет функции, вызываемой тестируемой функцией того же файла с помощью jest

#jestjs

#jestjs

Вопрос:

У меня есть файл, экспортирующий две функции a и b , где b выполняется вызов a .

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

functions.js

 export const a = (x) => { a very complicated function };

export const b = (x) => a(x 1);
  

functions.test.js

 import { a, b } from './functions';

describe('b', () => {
  test('calling b calls a with x 1', () => {
    const fakeA = //mock function a ... don't know how to.
    b(1);
    expect(fakeA).toHaveBeenCalledWith(2);
  });
});

  

Комментарии:

1. Ты не можешь этого сделать. Либо разделите их на разные модули, либо протестируйте как единое целое.

2. a является деталью реализации b . В тестах вы даже не знаете, что он существует. Или, если задокументированное поведение b зависит от a , то a должно быть предоставлено в качестве аргумента b (и вы можете предоставить макет в тестах).

3. Чтобы переформулировать мой предыдущий комментарий: предполагая, что я должен использовать функцию b , нужно ли мне что-либо знать о a (для использования b )? a Есть ли какие-то побочные эффекты, о которых я должен заботиться при использовании b ? Если ответ «нет», то a это деталь реализации b , и тест, который вы написали в вопросе, следует удалить. Это не вариант использования, это не помогает вам разрабатывать b ; это мешает вам изменять b реализацию.

4. @axiac, ну, в моем реальном случае a должен вызываться с множеством параметров, которые я не хочу издеваться, и у него также есть множество побочных эффектов, таких как отправка sms и электронных писем. Поэтому мне действительно нужно протестировать эти b вызовы a с ожидаемыми параметрами, не вызывая фактические a .

5. Похоже, a делает слишком много вещей. Я предполагаю, что b это сложнее, чем описано в вопросе, но, тем не менее, тот факт, что он вызывает a , является просто деталью реализации, а не ее результатом. Его результатом является электронная почта, SMS и, возможно, другие. Вариант использования должен ожидать вызова функции, которая отправляет электронное письмо.

Ответ №1:

После долгих исследований я нашел 2 способа добиться этого :

  • Первый способ — вызвать exports.a вместо a в b функции :

functions.js

 export const a = (x) => { a very complicated function };

export const b = (x) => exports.a(x 1);
  

functions.test.js

 import * as functions from './functions';

describe('b', () => {
  test('calling b calls a with x 1', () => {
    functions.a = jest.fn();
    functions.b(1);
    expect(functions.a).toHaveBeenCalledWith(2);
  });
});

});
  
  • Второй способ — изменить прототип b на принимает функцию, по умолчанию на a :

functions.js

 export const a = (x) => { a very complicated function };

export const b = (x, a = exports.a) => a(x   1);
  

functions.test.js

 import { a, b } from './functions';

describe('b', () => {
  test('calling b calls a with x 1', () => {
    const fakeA = jest.fn();
    b(1, fakeA);
    expect(fakeA).toHaveBeenCalledWith(2);
  });
});
  

Комментарии:

1. Возвращаясь к этой проблеме снова, с этим первым подходом, как мне сбросить реализацию функции в исходную функцию, потому что последующие тесты, для которых требуется исходная функция, считают ее пустой функцией для functions.a = jest.fn(); Я пробовал clearAllMocks и resetAllMocks, и она не сбрасывается

Ответ №2:

Самый простой способ решить эту проблему — использовать метод jest.spyOn.

jest.spyOn(объект, имяметода)

Создает макет функции, похожей на jest.fn, но также отслеживает вызовы object[methodName]. Возвращает макет функции Jest.

 import * as functions from '../functions';


describe('b', () => {
  test('calling b calls a with x 1', () => {

    const fakeA = jest.spyOn( functions, 'a' );

    functions.b(1);

    expect( fakeA ).toHaveBeenCalledWith(2);
  });
});
  

Комментарии:

1. являются ли эти файлы файлами js или ts?

2. Невозможно шпионить за функцией, которая используется в том же модуле, в котором она была определена.