Submit форм и обработчики

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();
});

Организация тестов форм

  1. Отдельные тесты для валидации — проверка ошибок при пустых или некорректных значениях.
  2. Тесты успешного сабмита — проверка вызова обработчика и отображения данных.
  3. Тесты взаимодействий — ввод текста, клики по чекбоксам и радио, выбор из селектов.

Структурирование тестов позволяет точно определить, какая часть формы ведет себя некорректно при изменении кода.


Полезные советы

  • Предпочтительно взаимодействовать с элементами так, как это сделал бы пользователь, а не через прямое изменение состояния.
  • Проверять доступность элементов с помощью ролей и aria-label.
  • Использовать userEvent для более реалистичного моделирования событий.
  • Асинхронные проверки всегда оборачивать в await findBy* или waitFor.
  • Мокировать функции сабмита с помощью jest.fn(), чтобы изолировать тест от сетевых запросов.

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