Мокирование как хорошая практика тестирования фронтенда

Привет, я Фируз Шоев, тестировщик в Отелло — сервисе бронирования отелей от 2ГИС. Мы в стадии активного роста и постоянно усиливаем команду. Несколько месяцев назад я подключился к найму тестировщиков и с тех пор провожу собеседования практически каждую неделю. За это время я стал замечать, что многие кандидаты не уделяют должного внимания тестированию фронтенда.

Почти в каждом втором собеседовании я слышу следующее:

  • Во фронтенде можно максимум вёрстку протестировать отдельно от бэкенда.

  • Фронтенд тестируем через end-to-end тесты, но они сложные и нестабильные.

  • А как вообще тестировать фронтенд изолированно?

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

В этой статье я хочу рассказать о важности тестирования фронтенда и пользе моков. Мокирование вносит весомый вклад в обеспечение качества приложений 2ГИС, поэтому надеюсь, что этот подход принесёт пользу и другим.

Зачем тестировать фронтенд?

Фронтенд — это не только вёрстка. Здесь также выполняется логика и проводятся вычисления, в которые может закрасться ошибка.

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

Скриншот из приложения Отелло

Скриншот из приложения Отелло

Вот что должен выполнять этот компонент:

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

  • Отображать предложения в соответствующих категориях и с правильными иконками.

  • Закрывать поп-ап при нажатии на предложение или кнопку закрытия.

  • Очищать поле ввода при нажатии на соответствующую кнопку.

  • Обрабатывать ошибки и задержки ответов от бэкенда ожидаемым образом.

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

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

Тестирование фронтенда на моках

Просто отсоединить бэкенд от фронтенда мы не можем. Точнее можем, но тогда мало что получится протестировать. Мы столкнёмся с множеством ошибок, а в лучшем случае — с пустыми экранами, так как фронтенд не будет получать необходимых данных.

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

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

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

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

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

Изолированное тестирование и стабильность среды. Моки позволяют протестировать фронтенд независимо от состояния реального бэкенда. Стабилизация среды тестирования обеспечивает более консистентные результаты тестов.

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

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

Недостатки

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

Тестирование на моках не заменяет проверки интеграций. К примеру, проблемы могут возникнуть, если и фронтенд-приложение и моки ориентируются на неправильную или устаревшую версию контракта с бэкендом. В таком случае высока вероятность, что ошибку мы заметим только во время E2E тестирования (в интеграции с настоящим бэкендом).

Инструменты для мокирования

В контексте тестирования фронтенда я выделю три категории инструментов для работы с моками: инструменты для юнит-тестов, браузерные расширения для ручного тестирования и отдельные мок-серверы.

Инструменты для юнит-тестов позволяют проверять отдельные куски кода, ответственные за HTTP-запросы. Их полезно использовать в рамках white-box тестирования. К примеру, есть отдельная библиотека axios-mock-adapter и фреймворк для тестирования jest с аналогичными возможностями.

Скриншот из официальной документации axios-mock-adapter

Скриншот из официальной документации axios-mock-adapter

Браузерные расширения подходят для быстрой отладки во время разработки и ручного тестирования. Они работают в большинстве известных браузеров. 
Самые популярные расширения: requestly и tweak.

Скриншот браузерного расширения tweak

Скриншот браузерного расширения tweak

Мок-серверы похожи на реальный бэкенд. HTTP-запросы с фронтенда честно доходят до мок-сервера, а не перехватываются на полпути, как с браузерными расширениями, или на уровне кода приложения, как с инструментами для юнит-тестов. Также мок-серверы могут обрабатывать запросы не только от браузера, но и от сервера. Это полезно, если фронтенд-приложение использует Server-Side Rendering для формирования веб-страницы на стороне своего сервера. 

Известные мок-серверы с открытым исходным кодом: wiremock, mockserver, mockoon.

Скриншот из официальной документации mockserver

Скриншот из официальной документации mockserver

Что используем мы

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

Наш основной инструмент для имитации ответов от сервера в UI-автотестах — это Python-библиотека JJ. Мы пишем тесты на Python, поэтому она органично встроилась в наш проект.

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

Схема взаимодействия приложения, тестов и мок-сервера на нашем проекте выглядит так:

Схема взаимодействия приложения, тестов и мок-сервера

Схема взаимодействия приложения, тестов и мок-сервера

Сначала тесты готовят данные на мок-сервере. Затем они нажимают кнопки в приложении. Приложение отправляет запросы на мок-сервер (точно так же, как на реальный бэкенд) и получает от него ответы с данными. Тесты проверяют не только визуальное состояние приложения, но и его запросы в истории моков.

Рекомендации тем, кто хочет попробовать мокирование

Если планируете внедрять тестирование на моках в свой проект, вот несколько рекомендаций:

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

  2. Дальше можно переходить к внедрению моков в автотесты: установить JJ, поднять мок-сервер и написать первый мок поможет гайд из официальной документации.

  3. Мокирование предполагает подготовку тестовых данных. Мы генерируем их с помощью Python-библиотеки d42. Для нее тоже есть гайд с примерами использования.

  4. Важно поддерживать моки в соответствии с API-контрактами и не пренебрегать проверками интеграции с настоящим бэкендом.

Если у вас остались вопросы, буду рад ответить в комментариях. А если захотите побольше узнать об RnD в 2ГИС — заглядывайте в наш Телеграм-канал.

© Habrahabr.ru