Тестирование actions и selectors

React Testing Library — это инструмент, ориентированный на тестирование компонентов React с акцентом на пользовательское взаимодействие. Вместо того чтобы проверять внутреннюю логику компонентов, библиотека предлагает подход, при котором тесты имитируют поведение реального пользователя, работая с компонентами через доступные элементы интерфейса. Два важнейших аспекта взаимодействия с компонентами — это actions (действия) и selectors (селектора), которые играют ключевую роль при тестировании.

Работа с actions

Действия (actions) — это те операции, которые могут быть выполнены пользователем в интерфейсе, такие как клики, ввод текста, наведение мыши и другие взаимодействия с элементами UI. React Testing Library предоставляет набор утилит для имитации этих действий. Например, для кликов используется функция fireEvent, а для ввода текста — fireEvent.change.

1. Симуляция кликов

Клик по элементу — одно из самых распространённых действий, которое необходимо протестировать. Для имитации клика используется метод fireEvent.click.

Пример:

import { render, screen, fireEvent } from '@testing-library/react';
import MyButton from './MyButton';

test('Button click triggers action', () => {
  render(<MyButton />);
  
  const button = screen.getByRole('button');
  fireEvent.click(button);
  
  // Пример проверок после клика
  expect(screen.getByText(/Кнопка нажата!/)).toBeInTheDocument();
});

В данном примере мы рендерим компонент MyButton, находим кнопку с помощью getByRole('button') и инициируем клик с помощью fireEvent.click. После клика проверяется, что текст на странице изменился.

2. Ввод текста

Для имитации ввода текста в поля формы используется событие fireEvent.change. Это событие позволяет симулировать изменения значения в элементе input.

Пример:

import { render, screen, fireEvent } from '@testing-library/react';
import MyForm from './MyForm';

test('Input changes on user typing', () => {
  render(<MyForm />);
  
  const input = screen.getByLabelText('Введите текст');
  fireEvent.change(input, { target: { value: 'Hello, world!' } });
  
  expect(input.value).toBe('Hello, world!');
});

Здесь имитируется изменение значения в input. Метод fireEvent.change позволяет задать новое значение для поля формы. В тесте проверяется, что значение в поле изменилось.

3. Наведение мыши

Для тестирования эффектов наведения используется событие fireEvent.mouseOver. Это может быть полезно для проверки функциональности всплывающих подсказок, меню или других интерфейсных элементов, зависящих от наведения.

Пример:

import { render, screen, fireEvent } from '@testing-library/react';
import Tooltip from './Tooltip';

test('Tooltip appears on hover', () => {
  render(<Tooltip />);
  
  const element = screen.getByText('Наведи на меня');
  fireEvent.mouseOver(element);
  
  expect(screen.getByText('Подсказка')).toBeInTheDocument();
});

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

Работа с selectors

Селекторами называют методы, с помощью которых можно найти элементы на странице. React Testing Library предлагает множество способов поиска элементов: по роли, по тексту, по меткам, по тестовым ID и другим признакам. Важно выбрать подходящий способ в зависимости от контекста, так как каждый из них имеет свои особенности и области применения.

1. getByRole

Метод getByRole является одним из самых мощных инструментов для поиска элементов, так как он ориентирован на доступность. Он ищет элементы по их роли (например, кнопка, поле ввода, ссылка и т.д.). Каждый элемент в веб-странице имеет определённую роль, которая определяется атрибутами ARIA или HTML5 тегами.

Пример:

import { render, screen } from '@testing-library/react';
import MyButton from './MyButton';

test('Find button by role', () => {
  render(<MyButton />);
  
  const button = screen.getByRole('button');
  expect(button).toBeInTheDocument();
});

В этом примере метод getByRole используется для поиска кнопки. Важно помнить, что getByRole ищет только те элементы, которые соответствуют стандартам доступности, и это помогает создавать более доступные интерфейсы.

2. getByText

Метод getByText используется для поиска элементов по их текстовому содержимому. Этот способ полезен для поиска элементов, отображающих текст, такой как кнопки, заголовки, параграфы и другие текстовые блоки.

Пример:

import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';

test('Text content is displayed', () => {
  render(<MyComponent />);
  
  const textElement = screen.getByText(/Привет мир!/);
  expect(textElement).toBeInTheDocument();
});

В данном случае мы ищем элемент, содержащий текст “Привет мир!”. Метод getByText позволяет гибко искать текстовые элементы с учётом регистрозависимости или с использованием регулярных выражений.

3. getByLabelText

Метод getByLabelText позволяет находить элементы формы (например, input) по связанным с ними меткам (label). Это особенно важно для тестирования доступности и обеспечения правильной ассоциации между меткой и полем ввода.

Пример:

import { render, screen } from '@testing-library/react';
import MyForm from './MyForm';

test('Find input by label', () => {
  render(<MyForm />);
  
  const input = screen.getByLabelText('Имя');
  expect(input).toBeInTheDocument();
});

Здесь поиск осуществляется через текст, связанный с меткой элемента формы. Это важный момент для обеспечения доступности и соответствия стандартам.

4. getByTestId

Метод getByTestId используется для поиска элементов по атрибуту data-testid. Это удобный способ для поиска элементов в тестах, но его следует использовать с осторожностью, поскольку он может нарушать принципы доступности.

Пример:

import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';

test('Find element by test ID', () => {
  render(<MyComponent />);
  
  const element = screen.getByTestId('unique-element');
  expect(element).toBeInTheDocument();
});

В этом случае поиск осуществляется по значению атрибута data-testid. Этот метод полезен, если другие способы поиска элементов не подходят или если необходимо избежать изменения структуры документа.

Сложные селекторы и их комбинации

С помощью комбинации методов можно создавать более сложные селекторы для поиска элементов. Например, можно использовать getByRole вместе с name или hidden для получения более точных результатов, учитывая контекст интерфейса.

Пример:

import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';

test('Complex selector usage', () => {
  render(<MyComponent />);
  
  const button = screen.getByRole('button', { name: /Отправить/i });
  expect(button).toBeInTheDocument();
});

Здесь используется комбинация getByRole и name, что позволяет точнее указать, какой именно элемент должен быть найден (в данном случае — кнопка с текстом “Отправить”).

Резюме

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