React Testing Library (RTL) ориентирована на тестирование компонентов с точки зрения пользователя. Основной принцип — имитация действий пользователя через взаимодействие с DOM, а не внутренними методами компонентов. Формы и их обработчики являются ключевым элементом интерфейса, поэтому тестирование их поведения требует особого внимания.
RTL предоставляет несколько утилит для поиска элементов:
getByLabelText — находит элементы по ассоциированной с
ними <label> или атрибуту
aria-label.getByPlaceholderText — ищет элементы по
тексту-плейсхолдеру.getByRole — используется для элементов с конкретной
ролью, например, textbox, button,
checkbox.Примеры поиска:
const input = screen.getByLabelText('Имя пользователя');
const submitButton = screen.getByRole('button', { name: /отправить/i });
Использование ролей предпочтительно, так как оно ближе к восприятию пользователем и поддерживает доступность.
Для ввода текста в поля форм применяется userEvent.type
или fireEvent.change. Рекомендуется использовать
userEvent, так как он более точно имитирует
пользовательское поведение, включая последовательные события
keydown, keypress и input.
Пример:
import userEvent from '@testing-library/user-event';
const input = screen.getByLabelText('Email');
await userEvent.type(input, 'test@example.com');
expect(input).toHaveValue('test@example.com');
В случае fireEvent.change нужно вручную передавать
объект события:
fireEvent.change(input, { target: { value: 'test@example.com' } });
expect(input.value).toBe('test@example.com');
Для тестирования сабмита формы ключевой момент — имитация клика по
кнопке submit или события submit на форме. RTL
позволяет это сделать через userEvent.click или
fireEvent.submit.
Пример через кнопку:
const button = screen.getByRole('button', { name: /отправить/i });
await userEvent.click(button);
Пример через форму:
const form = screen.getByRole('form');
fireEvent.submit(form);
Для проверки вызова обработчика используется
jest.fn():
const handleSubmit = jest.fn();
render(<Form onSub mit={handleSubmit} />);
await userEvent.click(screen.getByRole('button', { name: /отправить/i }));
expect(handleSubmit).toHaveBeenCalledTimes(1);
После сабмита важно убедиться, что форма обрабатывает данные
корректно и отображает ошибки при некорректном вводе. Для этого
используется комбинация screen.getByText и ожидания
waitFor, если обработка асинхронная.
await userEvent.click(screen.getByRole('button', { name: /отправить/i }));
expect(await screen.findByText('Поле email обязательно')).toBeInTheDocument();
Асинхронные проверки часто применяются при работе с формами, подключенными к API или с задержкой валидации.
Для элементов типа checkbox и radio
используется userEvent.click:
const checkbox = screen.getByLabelText('Принять условия');
await userEvent.click(checkbox);
expect(checkbox).toBeChecked();
Для радио-кнопок аналогично:
const radio = screen.getByLabelText('Мужской');
await userEvent.click(radio);
expect(radio).toBeChecked();
При этом важно проверять, что только один радиоэлемент выбран в группе.
Для <select> лучше применять
userEvent.selectOptions:
const select = screen.getByLabelText('Страна');
await userEvent.selectOptions(select, 'Россия');
expect(select.value).toBe('Россия');
Такой подход учитывает правильное обновление состояния компонента и
триггерит событие change.
Современные формы часто используют асинхронные сабмиты. Для
корректного тестирования применяются async/await и утилиты
RTL:
findBy* — ожидание появления элемента.waitFor — ожидание определенного состояния DOM.Пример:
await userEvent.type(screen.getByLabelText('Email'), 'test@example.com');
await userEvent.click(screen.getByRole('button', { name: /отправить/i }));
await waitFor(() => {
expect(screen.getByText('Спасибо за регистрацию')).toBeInTheDocument();
});
Структурирование тестов позволяет точно определить, какая часть формы ведет себя некорректно при изменении кода.
aria-label.userEvent для более реалистичного
моделирования событий.await findBy*
или waitFor.jest.fn(), чтобы
изолировать тест от сетевых запросов.Эти принципы обеспечивают надёжное тестирование форм и обработчиков в React, гарантируя, что пользовательский интерфейс работает корректно при любых сценариях взаимодействия.