Что такое издеваться?

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

Зависимостью может быть все, от чего зависит ваше устройство, но обычно это модуль, который модуль импортирует.

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

Короче говоря, мокинг — это создание юнита, который имитирует поведение реального юнита.

Обещаю больше не использовать в статье слово «юнит».

Тестовые двойники — манекен, подделка, заглушка, муляж, шпионы

В тестировании есть несколько определений объектов, которые не являются реальными. Общий термин — тестовый дубль.

Тестовый двойник — это объект, который может заменить реальный объект в тесте, подобно тому, как каскадер заменяет актера в кино.

Существует пять основных типов тестовых двойников: заглушка, фиктивный, поддельный, шпион, фиктивный.

Заглушки

Заглушка — это объект, который содержит предопределенные данные и использует их для ответа на вызовы во время тестов. Он используется, когда мы не можем или не хотим задействовать объекты, которые будут отвечать реальными данными или иметь нежелательные побочные эффекты.

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

Мокает

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

Макеты — это объекты, которые регистрируют полученные вызовы.

В тестовом утверждении мы можем проверить Mocks, что все ожидаемые действия были выполнены.

Мы используем макеты, когда не хотим вызывать производственный код или когда нет простого способа проверить, был ли выполнен предполагаемый код. Нет возвращаемого значения и нет простого способа проверить изменение состояния системы. Примером может быть функциональность, которая вызывает службу отправки электронной почты.

Мы не хотим отправлять электронные письма каждый раз, когда запускаем тест. Кроме того, в тестах непросто проверить правильность отправки письма. Единственное, что мы можем сделать, это проверить результаты функциональности, используемой в нашем тесте. В других случаях убедитесь, что была вызвана служба отправки электронной почты.

Фальшивый

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

Шпионы

Шпионы — это заглушки, которые также записывают некоторую информацию в зависимости от того, как они были вызваны. Одной из форм этого может быть служба электронной почты, которая записывает, сколько сообщений было отправлено.

Дурачок

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

В большинстве реальных сценариев мы используем фиктивные макеты.

Пример тестового варианта использования

Здесь мы можем только проверить, возвращает ли thumbWar один из переданных ему параметров.

Это явно не комплексный тест.

Итак, мы создадим макет функции getWinner.

Самый простой вид насмешек — это обезьянье исправление.

Обезьянье исправление — это метод добавления, изменения или подавления поведения фрагмента кода по умолчанию во время выполнения без изменения исходного исходного кода.

Итак, здесь мы имитируем реализацию функции getWinner после ее импорта, а затем восстанавливаем исходную реализацию после завершения теста.

Однако это не рекомендуется, потому что здесь мы присваиваем какое-то значение методу импортированного модуля. Это нарушает правило eslint — import/namespace.

Это не очень хорошая идея.

Примечание: Jest, по сути, делает то же самое и с имитацией, но выполняет некоторую магию в фоновом режиме и имитирует модульную систему, что делает код выглядящим совместимым, и мы не получаем никаких предупреждений/. ошибки

Насмешка с шуткой

Когда мы говорим о мокировании в Jest, мы обычно говорим о замене зависимостей с помощью мок-функции jest.

Функция Mock позволяет тестировать связи между кодом,

  • стирание фактической реализации функции,
  • захват вызовов функции (и параметров, переданных в этих вызовах),
  • захват экземпляров функций-конструкторов при создании с помощью new,
  • и разрешить настройку возвращаемых значений во время тестирования

Цель насмешек — заменить то, что мы не контролируем, тем, что мы делаем, поэтому важно, чтобы то, чем мы это заменяем, обладало всеми необходимыми функциями.

Функция Mock предоставляет следующие возможности:

  • Захват звонков
  • Установить возвращаемые значения
  • Изменить реализацию

Мокающие утилиты в Jest

jest.fn()

Самый простой способ создать экземпляр фиктивной функции — использовать jest.fn().

Здесь у нас есть куча полезных функций для сложения, вычитания, умножения и деления переданных параметров.

Мы импортируем вспомогательные функции из math.js и используем их здесь.

Теперь, чтобы протестировать функции doAdd, и doSubtract, нам нужно создать макеты для функций сложения и вычитания с помощью jest.fn().

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

Как я упоминал, когда мы проходили исправление обезьяны, это очень похоже на то, что мы делали там, но здесь jest фактически имитирует для нас модульную систему и делает некоторую магию, чтобы наш код выглядел так, как будто наш код соответствует спецификации, поэтому мы не получайте никаких ошибок eslint.

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

Шпионская шутка

Другой метод создания макета функции — метод jest.spyOn(). Подобно jest.fn(), он создает управляемый макет.

Ключевое отличие заключается в том, что по умолчанию вызывается исходная реализация.

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

В этих случаях вы можете использовать jest.spyOn

jest.spyOn() хранит в памяти исходную реализацию, поэтому, если она была переопределена, jest.spyOn() позволяет восстановить исходную реализацию с помощью mockRestore().

Простое использование jest.SpyOn().

Здесь мы издеваемся над функцией, но затем восстанавливаем исходную реализацию с помощью mockRestore.

Примечание. MockRestore полезен для тестов в одном и том же файле, но его не нужно делать в хуке afterAll, поскольку каждый тестовый файл в Jest изолирован.

Главное, что нужно помнить о jest.spyOn, это то, что это просто сахар для базового использования jest.fn().

Мы можем достичь той же цели, сохранив исходную реализацию, установив для фиктивной реализации значение original и переназначив оригинал позже:

Здесь, в начале теста, мы сохраняем функцию math.add в переменной originalAdd.

Затем мы имитируем реализацию для math.add и тестируем фиктивную реализацию.

В конце мы восстанавливаем math.add обратно в originalAdd .

По сути, это то, что делает jest.spyOn().

Разница между использованием jest.fn() и jest.spyOn()

Как таковых явных различий нет, мы используем их в разных сценариях.

шутка.fn()

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

шутка.spyOn()

  • Первоначальная реализация функции актуальна для вашего теста, но,
  • Вы хотите добавить свою собственную реализацию только для определенного сценария, а затем снова сбросить ее с помощью mockRestore() (если вы просто используете jest.spyOn() без дальнейшего издевательства, она все равно будет вызывать исходную функцию по умолчанию)
  • Вы просто хотите увидеть, была ли вызвана функция

Издевательство над модулем

Мы также можем имитировать весь модуль, используя jest с Jest.mock .

Когда мы имитируем модуль, jest просто перехватывает вызовы всех функций этого модуля и заменяет их имитируемыми функциями.

Здесь мы издеваемся над модулем Math.js.

Таким образом, вызов jest.mock(‘./math.js’) по существу устанавливает для math.js:

Это полезно в тех случаях, когда нам нужно протестировать несколько функций одного и того же модуля. Таким образом, нам не придется издеваться над всеми функциями по отдельности.

Имитация API

  • Довольно просто имитировать ответы API с помощью jest.mock().

Пример

Здесь, когда мы имитируем модуль axios, все функции, предоставляемые axios, заменяются на jest.fn().

Затем мы возвращаем фиктивный ответ из метода axios.get, используя mockResolvedValue.

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

Есть несколько проблем с тестированием вызовов API, подобных этому.

  • На самом деле мы не делаем здесь вызов API. Мы имитируем обработчик API и заглушаем фиктивный ответ для метода get обработчика.
  • Это оставляет нас открытыми для множества ошибок, которые могут возникнуть во время фактического вызова API. Поскольку все, что мы здесь делаем, — просто заглушаем фиктивный ответ, мы не можем проверить, передает ли пользователь требуемые заголовки, или запрашивать параметры у API. Предположим, вы делаете почтовый запрос, и API изменился, чтобы принимать «данные» вместо «тело». Этот сценарий не может быть протестирован здесь.
  • Мы издеваемся над аксиосами. Итак, теперь это зависимость для нашего теста. В будущем, если мы решим перейти от аксиом к выборке, наши тесты начнут ломаться. Это потому, что мы издеваемся над деталями реализации.

Все эти проблемы можно решить, смоделировав обработчик API на более детальном уровне и протестировав все эти сценарии, но это будет очень утомительно.

Здесь на помощь приходит MSW

ТБО

MSW расшифровывается как фиктивный сервисный работник. Это библиотека для имитации API, которая использует Service Worker API для перехвата реальных запросов.

Апи перехватывается на сетевом уровне, поэтому наше приложение ничего не знает о мокке, а вызов апи на самом деле делает, это видно даже во вкладке сетей. Ответ только что возвращен нашим сервером msw.

Основная идея такова: создайте фиктивный сервер, который перехватывает все запросы и обрабатывает их так же, как если бы это был настоящий сервер.

Здесь мы вызываем функцию setupWorker msw и передаем обработчик запросов рабочему процессу.

мы используем обработчик запросов на получение, поэтому теперь, после запуска рабочего процесса, он будет перехватывать все вызовы get API, сделанные в конечную точку /albumTitles, и обрабатывать их.

Примечание. У него есть обработчики всех функций rest, а также graphQl.

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

Это принимает три аргумента

  • req — объект запроса (req) хранит информацию о соответствующем запросе. Можно использовать это, если вы хотите получить доступ к некоторому свойству из объекта запроса. Например, req.params или req.headers и т. д.
  • res — функциональная утилита для создания имитируемого ответа.
  • ctx — группа функций, которые помогают установить код состояния, заголовки, тело и т. д. имитируемого ответа.

Примечание. MockRestore полезен для тестов в одном и том же файле, но его не нужно делать в хуке afterAll, поскольку каждый тестовый файл в Jest изолирован.

Кредит на фрагменты кода/примеры для насмешек: https://kentcdodds.com/blog/but-really-what-is-a-javascript-mock