Тестирование checkboxes и radio buttons

Работа с формами в React часто подразумевает использование checkboxes и radio buttons для выбора нескольких или единственного значения. Правильное тестирование этих элементов обеспечивает надежность интерфейса и корректную обработку пользовательских действий.

Выбор и идентификация элементов

React Testing Library ориентирована на взаимодействие с DOM так, как это делает пользователь. Для поиска чекбоксов и радиокнопок применяются методы:

  • getByRole('checkbox', { name: /название/i })
  • getByRole('radio', { name: /название/i })
  • getByLabelText(/метка/i)

Использование роли предпочтительно, так как она учитывает доступность элемента, а getByLabelText связывает тест с визуальной меткой, что повышает читаемость.

const checkbox = screen.getByRole('checkbox', { name: /согласие/i });
const radioOption = screen.getByLabelText(/мужской/i);

Проверка состояния элементов

Для чекбоксов важно тестировать выбранное/невыбранное состояние:

expect(checkbox).not.toBeChecked(); // элемент не выбран
fireEvent.click(checkbox);
expect(checkbox).toBeChecked(); // элемент выбран после клика

Для радиокнопок проверяется, что только одна кнопка в группе может быть выбрана:

const maleRadio = screen.getByLabelText(/мужской/i);
const femaleRadio = screen.getByLabelText(/женский/i);

fireEvent.click(maleRadio);
expect(maleRadio).toBeChecked();
expect(femaleRadio).not.toBeChecked();

fireEvent.click(femaleRadio);
expect(femaleRadio).toBeChecked();
expect(maleRadio).not.toBeChecked();

Использование userEvent вместо fireEvent

userEvent имитирует поведение пользователя более реалистично, учитывая события pointer, focus и change:

import userEvent from '@testing-library/user-event';

await userEvent.click(checkbox);
expect(checkbox).toBeChecked();

await userEvent.click(femaleRadio);
expect(femaleRadio).toBeChecked();
expect(maleRadio).not.toBeChecked();

Тестирование нескольких чекбоксов

Часто необходимо проверить набор чекбоксов:

const options = screen.getAllByRole('checkbox');

await userEvent.click(options[0]);
await userEvent.click(options[2]);

expect(options[0]).toBeChecked();
expect(options[1]).not.toBeChecked();
expect(options[2]).toBeChecked();

Такой подход позволяет убедиться, что выбор нескольких элементов работает независимо.

Проверка обработчиков событий

Важно удостовериться, что onChange вызывается корректно:

const handleChange = jest.fn();
render(<input type="checkbox" onCha nge={handleChange} />);

await userEvent.click(screen.getByRole('checkbox'));
expect(handleChange).toHaveBeenCalledTimes(1);

Для радиокнопок тест аналогичен:

const handleRadioChange = jest.fn();
render(
  <>
    <input type="radio" value="a" name="group" onCha nge={handleRadioChange} />
    <input type="radio" value="b" name="group" onCha nge={handleRadioChange} />
  </>
);

await userEvent.click(screen.getByDisplayValue('b'));
expect(handleRadioChange).toHaveBeenCalledTimes(1);

Асинхронные изменения состояния

Если выбор чекбокса или радиокнопки запускает асинхронное действие (например, загрузку данных), необходимо использовать waitFor:

await userEvent.click(checkbox);

await waitFor(() => expect(screen.getByText(/загрузка завершена/i)).toBeInTheDocument());

Это гарантирует, что тест не завершится до выполнения всех обновлений состояния.

Проверка доступности (a11y)

Checkboxes и radio buttons должны иметь корректные метки (label) для обеспечения доступности. Тесты через getByLabelText одновременно проверяют и эту доступность.

const checkbox = screen.getByLabelText(/принимаю условия/i);
expect(checkbox).toBeInTheDocument();

Именование и структура тестов

Для ясности тесты обычно структурируют по сценариям:

  • Выбор и снятие выбора чекбокса
  • Выбор радиокнопки и проверка эксклюзивности
  • Обработка нескольких чекбоксов
  • Вызов обработчиков событий
  • Асинхронные эффекты после выбора

Это упрощает поддержку и делает тесты читаемыми.

Лучшие практики

  • Предпочтительно использовать userEvent вместо fireEvent для имитации действий пользователя.
  • Проверять состояние через toBeChecked или not.toBeChecked, а не напрямую через checked.
  • Для радиокнопок проверять, что выбран только один элемент группы.
  • Тестировать не только состояние, но и вызовы обработчиков событий.
  • В асинхронных сценариях использовать waitFor для корректного ожидания изменений.

Эти подходы делают тесты надежными, доступными и максимально приближенными к реальному поведению пользователей.