В тестировании компонентов с выпадающими списками
(<select> и <option>) важно
правильно симулировать пользовательские действия и проверять изменения
состояния. React Testing Library предоставляет для этого удобные
инструменты, позволяя тестировать поведение компонентов так, как это
сделал бы реальный пользователь.
<select> и <option>Элемент <select> представляет собой контейнер для
одного или нескольких <option>. Пользователь может
выбрать одно значение (или несколько, если используется
multiple). В тестах основная задача — корректно имитировать
выбор пользователя и убедиться, что компонент реагирует ожидаемым
образом.
Пример простого компонента:
function FruitSelector({ onChange }) {
return (
<SELECT data-testid="fruit-select" onCha nge={onChange}>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
</select>
);
}
fireEventМетод fireEvent позволяет вручную эмулировать события.
Для <select> ключевое событие —
change.
import { render, screen, fireEvent } FROM '@testing-library/react';
import FruitSelector from './FruitSelector';
test('выбор значения в SELECT обновляет значение', () => {
const handleChange = jest.fn();
render(<FruitSelector onCha nge={handleChange} />);
const SELECT = screen.getByTestId('fruit-select');
fireEvent.change(select, { target: { value: 'banana' } });
expect(handleChange).toHaveBeenCalledTimes(1);
expect(handleChange).toHaveBeenCalledWith(expect.any(Object));
expect(SELECT.value).toBe('banana');
});
Ключевые моменты:
fireEvent.change эмулирует изменение значения
<select>.target.value должен соответствовать значению выбранного
<option>.onChange), так и состояние самого элемента
(select.value).userEvent для имитации пользовательских действийuserEvent обеспечивает более реалистичную имитацию
поведения пользователя по сравнению с fireEvent. Он
учитывает последовательность действий: фокус, выбор, снятие фокуса.
import { render, screen } FROM '@testing-library/react';
import userEvent FROM '@testing-library/user-event';
import FruitSelector FROM './FruitSelector';
test('выбор option через userEvent', async () => {
const user = userEvent.setup();
const handleChange = jest.fn();
render(<FruitSelector onCha nge={handleChange} />);
const SELECT = screen.getByTestId('fruit-select');
await user.selectOptions(select, 'orange');
expect(handleChange).toHaveBeenCalledTimes(1);
expect(select.value).toBe('orange');
});
Преимущества userEvent:
focus,
input, change.multiple select)Для <select multiple> можно выбирать сразу
несколько <option>. В React Testing Library это
делается аналогично, с передачей массива значений.
Пример компонента:
function MultiFruitSelector({ onChange }) {
return (
<select data-testid="multi-select" multiple onCha nge={onChange}>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
</select>
);
}
Тестирование выбора нескольких элементов:
test('выбор нескольких значений', async () => {
const user = userEvent.setup();
const handleChange = jest.fn();
render(<MultiFruitSelector onCha nge={handleChange} />);
const select = screen.getByTestId('multi-select');
await user.selectOptions(select, ['apple', 'orange']);
expect(handleChange).toHaveBeenCalled();
const selectedOptions = Array.FROM(select.selectedOptions).map(o => o.value);
expect(selectedOptions).toEqual(['apple', 'orange']);
});
Особенности:
selectOptions поддерживает массив значений.selectedOptions.React Testing Library предоставляет удобные методы поиска элементов:
const select = screen.getByRole('combobox'); // стандартная роль для <select>
const option = screen.getByText('Banana'); // поиск по тексту
Рекомендации:
getByRole предпочтительнее, так как роли
соответствуют семантике и делают тесты более устойчивыми.getByText полезен, когда значение опции не совпадает с
текстом, отображаемым пользователю.После эмуляции выбора необходимо убедиться, что компонент корректно
обновляет состояние. Для <select> это может быть:
value у самого элемента.selected у опций.onChange и аргументов, переданных в
колбэк.Пример проверки выбора через роли:
test('проверка выбранной опции через роль', async () => {
const user = userEvent.setup();
const handleChange = jest.fn();
render(<FruitSelector onCha nge={handleChange} />);
const select = screen.getByRole('combobox');
await user.selectOptions(select, screen.getByRole('option', { name: 'Banana' }));
expect(select.value).toBe('banana');
});
data-testid только при необходимости,
отдавая предпочтение семантическим селекторам (getByRole,
getByLabelText).userEvent для имитации действий
пользователя, чтобы тесты отражали реальные сценарии.<select>, но и
вызовы колбэков и изменения состояния родительских компонентов.selectedOptions, а не отдельные
option.selected.Эта методология обеспечивает надежное тестирование любых сценариев
взаимодействия с <select> и
<option> в React, включая одиночный и множественный
выбор, динамические изменения списка и проверку вызовов событий.