Как тестировать React-приложения

Как тестировать React-приложения

KOD

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

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

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

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

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


Можно сначала написать тесты для компонента Friend - проверить, отображает ли он корректно все филды и выполняются ли нормально все условия во время рендеринга. Если у элемента списка есть кнопка "Убрать из друзей", мы можем в юнит тесте компонента замокать (от сл. mock) хэндлер кнопки и проверить, вызвался ли он при нажатии. Но в этом, по факту, нет никакого смысла, потому что вам всё равно придется тестировать FriendsList и проверять как работает логика отправки запроса на бэк и реакция на запрос на нашей стороне.

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

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

MWS JS - библиотека для тестирования API-запросов

Есть два варианта тестирования запросов на бэк:

  1. мок-объект функции отправки запроса, например, axios.get или fetch.
  2. Мок API

Для мока API используют Mock Service Worker. С помощью него можно легко создать фейковые эндпоинты как для автоматических тестов, так и для тестирования непосредственно в браузере.

Теперь немного о стейте Redux. Я использую во всех своих компонентах общих baseState, где храню базовый стейт приложения. Его прокидывают в кастомный render метод React Testing Library.


Используют его так:


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

Если бы наш компонент FriendsList из примера в начале статьи делал запросы на бэкенд через thunk и хранил весь стейт в редаксе:


Тест для такого компонента выглядел бы следующим образом (Удаляем Дэниела из друзей).


Такой тест будет работать, если мы не забыли замокать наше API. В конкретном случае baseState нам не понадобился. Но если бы нам для удаления друга понадобилось дополнительно отправлять запрос на другой эндпоинт с именем текущего юзера, baseState в thunk бы нам пригодился.

Асинхронность React Testing Library (RTL)

Когда вы делаете запросы на бэкенд, после этих действий обязательно необходимо использовать await findBy, чтобы RTL ждал окончания запроса. Для обычного поиска элементов используем getBy или queryBy. Также вместо findBy можно использовать await waitFor(() => {}); При этом waitFor должен возвращать результат. Если вы ожидаете, что элемент удалится из DOM после запроса, необходимо использовать queryBy в связке с waitFor, потому что findBy кидает ошибку, если не находит элемент. Да, всё это выглядит запутано, и так оно и есть. Асинхронная логика в RT, запутана и на первых порах вы можете долго залипать в код, не понимая, почему RTL не ждёт выполнения запроса.

Для симуляции нажатия по кнопкам используют userEvent. Это отдельная библиотека, являющаяся частью RTL. Она кажется более близкой к реальному использованию приложения по логике работы.

Наверное, этого будет достаточно, чтобы вы примерно понимали, как работает тестирование приложений в React.

Report Page