Краткое руководство о том, как проверить, что функция зависит от другой функции, экспортируемой тем же модулем.
Эта проблема
У вас есть модуль, который экспортирует несколько функций. Одна из этих функций зависит от другой функции того же модуля.
export function foo () { ... } export function bar () { foo() }
Вы хотите утверждать, что при выполнении bar()
он также запускает выполнение foo()
.
Это может показаться классической ситуацией для использования функций Jest spyOn
или mock
. Следовательно, вы ожидаете, что сможете написать такой тест:
import * as myModule from './myModule'; test('calls myModule.foo', () => { const fooSpy = jest.spyOn(myModule, 'foo'); myModule.bar(); expect(fooSpy).toHaveBeenCalledTimes(1); });
Удивительно или нет, но этот тест завершился неудачно с сообщением Expected mock function to have been called one time, but it was called zero times.
:
Вы можете попробовать использовать jest.mock()
или любой другой интерфейс Jest, чтобы утверждать, что ваш bar
метод зависит от вашего foo
метода.
В конечном итоге вы обвините Jest в том, что он вызвал ошибку, и пожалеете о том, что решили начать писать свои тесты с его помощью.
По правде говоря, это не про шутку. Речь идет о самом JavaScript.
Причина
Более подробно, это из-за того, как Javascript компилируется babel. Это результат компиляции myModule
:
var foo = function foo() {}; var bar = function bar() { foo(); }; exports.foo = foo; exports.bar = bar;
При объявлении функции bar
ссылка на функцию foo
включается в объявление функции.
В вашей тестовой среде, когда вы импортируете foo
и bar
, вы действительно импортируете exports.foo
и exports.bar
.
Следовательно, когда вы издеваетесь foo
, вы действительно издеваетесь exports.foo
.
При выполнении bar()
то, что вызывает bar
, - это вложенная ссылка на foo
.
Следовательно, тест не проходит правильно, поскольку exports.foo
никогда не вызывается при выполнении bar()
!
Решение (я)
Изучая информацию в Интернете, вы можете найти некоторые решения для преодоления этой проблемы с использованием функции require.
Теперь, чтобы быть точным, функция require
не является частью стандартного API JavaScript. Это встроенная функция среды Node.js, предназначенная для загрузки модулей.
Если вы, как и я, находите это решение нежелательным, существует два способа реструктуризации кода и проверки зависимости одной из функций от другой.
Единый источник правды
Первая стратегия, которую вы можете использовать, - это сохранение ссылок на ваши методы в объекте, который вы затем будете экспортировать. bar
вызовет ссылку на foo
, хранящуюся в этом объекте.
var foo = function foo() {}; var bar = function bar() { exportFunctions.foo(); }; const exportFunctions = { foo, bar }; export default exportFunctions;
Таким образом, вы импортируете и имитируете одну и ту же ссылку на foo
, который вызывается bar()
, и тот же тест, определенный ранее, теперь пройдет!
Разделение проблем
С другой стороны, вы можете разделить проблемы вашего кода и объявить две функции в двух разных модулях.
// fooModule.js export function foo () { ... } // barModule.js export function bar () { fooModule.foo() }
Это приведет к стандартному сценарию зависимости внешнего модуля.
Теперь вы можете адаптировать свой тест так, чтобы он был:
import foo from './fooModule'; import bar from './barModule'; test('calls fooModule.foo', () => { const fooSpy = jest.spyOn(fooModule, 'foo'); bar(); expect(fooSpy).toHaveBeenCalledTimes(1); });
Я надеюсь, что эта статья окажется для вас полезной на пути к счастливой и чистой доставке кода!
Спасибо моим коллегам Саша и Бретт aka Je (s) tt за поддержку и приятно проведенное время вместе во время исследования этой темы!