Тестирование в React: Jest, RTL, Cypress
Тестирование — одна из тех практик, которые отличают код, готовый к продакшену, от кода, который просто работает на вашем компьютере.
Без тестов любое изменение превращается в лотерею: непонятно, что сломается при следующем рефакторинге, а баг может всплыть уже у реальных пользователей. В этом уроке разберём, зачем нужны автоматические тесты и как проверять React-компоненты и целые пользовательские сценарии с помощью трёх инструментов — Jest, React Testing Library и Cypress.
Зачем нужно тестирование и какие виды тестов существуют
Три уровня тестов в экосистеме фронтенда
- Unit-тесты — проверяют отдельную функцию, хук или компонент в изоляции. Быстрые, дешёвые, их должно быть больше всего.
- Интеграционные тесты — проверяют, как несколько модулей работают вместе, например форма и её валидация.
- End-to-end (e2e) тесты — эмулируют реального пользователя в браузере: открывают страницу, кликают, заполняют поля. Медленные и дорогие в поддержке, но максимально приближены к реальности.
Для unit- и интеграционных тестов в React-мире стандартом стали Jest и React Testing Library, а для e2e — Cypress. Разберём каждый инструмент по отдельности.
Jest — фундамент модульного тестирования
Jest — это тест-раннер и библиотека утверждений в одном пакете.
Jest находит файлы с тестами, запускает их и показывает отчёт.
Если проект создан через Create React App или похожий шаблон, Jest уже настроен из коробки. В остальных случаях его можно установить вручную. Помимо React, Jest одинаково хорошо подходит для тестирования любого JavaScript-кода — от утилитных функций до серверных обработчиков.
npm install --save-dev jest
Структура теста
Тестовый файл обычно называют так же, как проверяемый модуль, с суффиксом .test.js. Внутри используются три ключевые функции: describe группирует связанные тесты, test (или его алиас it) описывает конкретную проверку, а expect формирует утверждение о результате.
function sum(a, b) {
return a + b;
}
describe('sum', () => {
test('складывает два положительных числа', () => {
expect(sum(2, 3)).toBe(5);
});
test('возвращает NaN для нечисловых аргументов', () => {
expect(sum('a', 3)).toBeNaN();
});
});
У Jest есть десятки матчеров — toBe, toEqual, toContain, toThrow и другие, которые покрывают почти любой сценарий проверки: сравнение примитивов, глубокое сравнение объектов, проверку исключений.
Моки и шпионы
Часто нужно проверить не результат функции, а то, что она была вызвана с определёнными аргументами. Для этого Jest предоставляет jest.fn() — функцию-заглушку, которая запоминает все вызовы, и jest.mock() — для подмены целых модулей, например API-клиента, чтобы тест не делал реальных сетевых запросов.
const onClick = jest.fn();
button.click();
expect(onClick).toHaveBeenCalledTimes(1);
React Testing Library — тестируем компоненты, а не детали реализации
React Testing Library (RTL) построена вокруг одной идеи: тест должен взаимодействовать с компонентом так же, как это делает пользователь, а не заглядывать во внутреннее состояние или пропсы. Это делает тесты устойчивыми к рефакторингу — можно поменять реализацию компонента, и тест не сломается, пока поведение для пользователя осталось прежним.
Основные инструменты — функция render, которая монтирует компонент в виртуальный DOM, и объект screen, через который ищутся элементы на странице.
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Counter from './Counter';
test('увеличивает счётчик при клике', async () => {
render(<Counter />);
const button = screen.getByRole('button', { name: /увеличить/i });
await userEvent.click(button);
expect(screen.getByText('Значение: 1')).toBeInTheDocument();
});
Для симуляции действий пользователя (клики, ввод текста, фокус) используется отдельный пакет @testing-library/user-event — он точнее эмулирует реальные события браузера, чем встроенный в Jest fireEvent.
Выбирайте запросы по приоритету, который рекомендует сама библиотека: сначала getByRole, затем getByLabelText и getByText, и только в крайнем случае getByTestId. Такой порядок заодно помогает находить проблемы с доступностью интерфейса.
Для элементов, которые появляются асинхронно — после запроса к серверу или задержки — есть семейство запросов findBy, которые возвращают промис и ждут появления элемента, вместо того чтобы падать сразу.
Cypress — end-to-end тестирование в браузере
В отличие от Jest и RTL, которые работают с виртуальным DOM, Cypress видит именно то, что увидел бы пользователь.
npm install --save-dev cypress
npx cypress open
Тесты в Cypress пишутся в файлах с расширением .cy.js и используют цепочку команд: cy.visit открывает страницу, cy.get и cy.contains ищут элементы, cy.click и cy.type эмулируют действия, а should формирует утверждение.
describe('Форма входа', () => {
it('пускает пользователя после ввода корректных данных', () => {
cy.visit('/login');
cy.get('[data-cy=email]').type('user@example.com');
cy.get('[data-cy=password]').type('secret123');
cy.get('[data-cy=submit]').click();
cy.url().should('include', '/dashboard');
});
});
Для запуска в CI без открытия окна браузера используется команда cypress run — она проходит все тесты в headless-режиме и возвращает код завершения, по которому можно понять, прошла ли сборка. Многие CI-системы умеют запускать эти тесты параллельно на нескольких машинах, что заметно ускоряет прогон объёмных наборов сценариев.
Не привязывайте селекторы Cypress к CSS-классам или структуре разметки — они меняются вместе с дизайном. Добавляйте отдельный атрибут вроде data-cy специально для тестов: он никак не влияет на стили и не сломается при рефакторинге вёрстки.
Как выбрать инструмент
Три инструмента не конкурируют между собой, а закрывают разные уровни пирамиды тестирования. Ниже — короткая шпаргалка, которая поможет быстро сориентироваться, что использовать в конкретной ситуации.
| Инструмент | Когда использовать |
|---|---|
| Jest | Проверка чистых функций, хуков и утилит без рендера DOM |
| React Testing Library | Проверка поведения компонента с точки зрения пользователя |
| Cypress | Проверка целых пользовательских сценариев в реальном браузере |
Итоги
Мы разобрали три уровня тестирования React-приложений: быстрые unit-тесты на Jest, тесты компонентов через React Testing Library, которые проверяют поведение с точки зрения пользователя, и end-to-end сценарии в реальном браузере на Cypress. Вместе эти инструменты закрывают почти любой сценарий проверки — от отдельной функции до целого пользовательского пути.
В следующем уроке поговорим об управлении состоянием приложения — разберём, какие подходы к State Management существуют в React и когда каждый из них уместен.