React Testing Library ориентирована на использование селекторов, которые имитируют поведение пользователя при взаимодействии с компонентами. Важно понимать, что некоторые селекторы могут быть уязвимыми к изменениям в структуре DOM. Это приводит к хрупким тестам, которые ломаются при незначительных изменениях в интерфейсе. Рассмотрим, как выбрать правильные селекторы для тестов и избежать таких проблем.
В React Testing Library селекторы позволяют находить элементы на странице для последующего взаимодействия с ними в тестах. Основные категории селекторов включают:
getByText,
queryByTextgetByRole,
queryByRolegetByLabelText,
queryByLabelTextgetByDisplayValue,
queryByDisplayValuegetByTestId,
queryByTestIdПри выборе селектора важно понимать, что он должен отражать реальное
поведение пользователя, а не зависеть от внутренней структуры
компонента. Например, поиск элементов по data-testid часто
приводит к хрупким тестам, поскольку идентификаторы могут изменяться без
изменения функциональности компонента.
Некоторые селекторы оказываются уязвимыми при изменении внутренней реализации компонента. Это связано с тем, что тесты ориентируются на технические детали, а не на то, как компонент воспринимается пользователем.
Пример хрупкого теста:
test('отображение кнопки с правильным текстом', () => {
const { getByTestId } = render(<Button data-testid="submit-button">Отправить</Button>);
const button = getByTestId('submit-button');
expect(button).toHaveTextContent('Отправить');
});
В этом примере тест зависит от data-testid, что делает
его уязвимым к изменениям в реализации компонента, например, при
изменении атрибута data-testid.
Использование ролей
Для большинства компонентов более устойчивыми являются селекторы, ориентирующиеся на роль элемента. HTML-элементы, такие как кнопки, поля ввода и ссылки, имеют заранее определённые роли, которые являются стабильными. Эти роли отражают их функциональность, а не внутреннюю структуру. Использование ролей делает тесты более гибкими и стойкими к изменениям.
test('кнопка должна отображать правильный текст', () => {
const { getByRole } = render(<Button>Отправить</Button>);
const button = getByRole('button');
expect(button).toHaveTextContent('Отправить');
});
Здесь тест ориентируется на роль элемента (button), что
делает его менее зависимым от внутренней реализации.
Использование меток
Селекторы по меткам (getByLabelText,
getByPlaceholderText) идеально подходят для взаимодействия
с формами. Они помогают находить элементы, основываясь на метках,
которые видит пользователь. Это обеспечивает более устойчивые тесты, так
как метки обычно не меняются без изменения бизнес-логики.
test('пользователь может ввести имя', () => {
const { getByLabelText } = render(<label htmlFor="name">Имя<input id="name" /></label>);
const input = getByLabelText(/имя/i);
fireEvent.change(input, { target: { value: 'Иван' } });
expect(input.value).toBe('Иван');
});
В данном случае тест использует метку Имя, что делает
его менее зависимым от внутренних идентификаторов.
Тестирование доступности
Роли и метки также важны для обеспечения доступности, так как они помогают вспомогательным технологиям (например, экранным читалкам) правильно интерпретировать структуру интерфейса. Это способствует не только стабильности тестов, но и улучшению пользовательского опыта для людей с ограниченными возможностями.
data-testidСелекторы, использующие атрибуты типа data-testid, могут
быть полезны в некоторых случаях, но их следует использовать с
осторожностью. Этот метод привязывает тесты к внутренним деталям
компонента, что может привести к проблемам при изменении реализации.
Использование data-testid может быть оправдано только в
следующих случаях:
Пример использования data-testid:
test('проверка отрисовки кнопки', () => {
const { getByTestId } = render(<Button data-testid="submit-button" />);
const button = getByTestId('submit-button');
expect(button).toBeInTheDocument();
});
Этот тест ломается, если в компоненте изменится атрибут
data-testid.
Рекомендуется строить тесты, основываясь на том, как компонент взаимодействует с пользователем. Для этого следует выбирать такие селекторы, которые отражают поведение пользователя, а не технические детали. Это может включать поиск по:
Такой подход позволяет тестам оставаться устойчивыми к изменениям, не затрагивающим функциональность.
Для улучшения тестов можно добавить проверки доступности (A11Y), проверяя, доступны ли элементы для пользователей с ограниченными возможностями. Это важно не только с точки зрения тестирования, но и для повышения качества приложения в целом.
React Testing Library имеет встроенные инструменты для тестирования
доступности, такие как axe-core, который можно
интегрировать с тестами для проверки нарушений стандартов
доступности.
Пример использования axe-core:
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('компонент соответствует стандартам доступности', async () => {
const { container } = render(<Button>Отправить</Button>);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Этот подход позволяет гарантировать, что компоненты остаются доступными и соответствуют стандартам доступности, минимизируя хрупкость тестов.
Хрупкие селекторы в React Testing Library могут быть причиной ненадёжных тестов, которые ломаются при малейших изменениях в реализации. Чтобы избежать этого, следует выбирать селекторы, ориентирующиеся на поведение пользователя, такие как роли, метки и текстовые содержимые. Эти подходы делают тесты более устойчивыми и легко поддерживаемыми, обеспечивая более высокое качество кода.