React Testing Library (RTL) предоставляет мощные средства для
тестирования взаимодействий с формами, включая выпадающие списки
(<select>) и кастомные dropdown-компоненты. Важным
аспектом является проверка не только визуального отображения, но и
корректного обновления состояния приложения после выбора элемента.
<select> и <option>Для начала необходимо корректно найти элементы на странице. RTL рекомендует ориентироваться на роль и текст, а не на классы или id:
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MyForm from './MyForm';
render(<MyForm />);
// Находим сам SELECT по роли
const selectElement = screen.getByRole('combobox');
// Проверка наличия конкретной опции
const optionElement = screen.getByRole('option', { name: 'Опция 1' });
Ключевой момент: для <select> роль всегда
combobox, а для <option> —
option. Это универсально для всех браузеров и
компонентов.
Выбор значения в <select> можно осуществить с
помощью userEvent.selectOptions, что корректно триггерит
все события (change, input) и обновляет
состояние:
import { render, screen } FROM '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MyForm from './MyForm';
render(<MyForm />);
const selectElement = screen.getByRole('combobox');
// Выбор одной опции
userEvent.selectOptions(selectElement, 'Опция 2');
// Проверка, что выбранная опция активна
expect(screen.getByRole('option', { name: 'Опция 2' }).selected).toBe(true);
Если нужно выбрать несколько опций в множественном SELECT
(<SELECT multiple>), можно передать массив:
userEvent.selectOptions(selectElement, ['Опция 1', 'Опция 3']);
expect(screen.getByRole('option', { name: 'Опция 1' }).selected).toBe(true);
expect(screen.getByRole('option', { name: 'Опция 3' }).selected).toBe(true);
Кастомные dropdown, реализованные через div/span и контролируемые
стейтом, не имеют стандартной роли combobox или
option. В таких случаях важно искать элементы по тексту или
test-id:
render(<CustomDropdown />);
// Открываем dropdown
const toggle = screen.getByText('Выберите элемент');
userEvent.click(toggle);
// Выбираем элемент
const item = screen.getByText('Элемент 2');
userEvent.click(item);
// Проверка обновленного значения
expect(screen.getByText('Элемент 2')).toBeInTheDocument();
Для кастомных dropdown часто требуется сначала симулировать клик по
кнопке открытия, а затем клик по элементу списка. RTL не имеет
встроенной функции selectOptions для таких случаев.
Если dropdown загружает данные асинхронно (например, с сервера),
необходимо использовать findBy или
waitFor:
import { render, screen, waitFor } FROM '@testing-library/react';
import userEvent FROM '@testing-library/user-event';
import AsyncDropdown from './AsyncDropdown';
render(<AsyncDropdown />);
userEvent.click(screen.getByText('Выберите элемент'));
// Ждем появления асинхронной опции
const option = await screen.findByText('Асинхронная опция');
userEvent.click(option);
expect(screen.getByText('Асинхронная опция')).toBeInTheDocument();
await screen.findByText автоматически ждет появления
элемента в DOM до таймаута по умолчанию. Использование
waitFor позволяет обернуть проверки, которые могут не
произойти мгновенно.
Очень важно проверять, что при выборе элемента вызываются правильные функции:
const handleChange = jest.fn();
render(<MyForm onCha nge={handleChange} />);
const selectElement = screen.getByRole('combobox');
userEvent.selectOptions(selectElement, 'Опция 1');
expect(handleChange).toHaveBeenCalledTimes(1);
expect(handleChange).toHaveBeenCalledWith(expect.any(Object));
Здесь проверяется, что обработчик события onChange
срабатывает один раз и получает объект события.
combobox и option для стандартных
<select>.data-testid, избегая зависимости от классов.findBy или
waitFor.change триггерятся только при
использовании userEvent.selectOptions, не
fireEvent.change — это гарантирует реалистичное
поведение.Использование этих подходов позволяет покрывать тестами все виды
dropdown-выборов, от простых <select> до сложных
кастомных компонентов с асинхронной подгрузкой данных, сохраняя
стабильность тестов и реалистичность пользовательских сценариев.