Queries: основной способ поиска элементов

React Testing Library (RTL) предоставляет набор методов для поиска и взаимодействия с элементами DOM в тестах. Основной принцип библиотеке — тестировать компоненты так, как пользователь взаимодействует с интерфейсом, а не опираться на внутренние реализации. В центре внимания находятся queries — функции поиска элементов.


Основные категории queries

Все методы поиска можно разделить на несколько категорий по степени предпочтительности:

  1. getBy — для поиска элементов, которые должны присутствовать в DOM. Если элемент не найден, тест падает с ошибкой.
  2. queryBy — для поиска элементов, которые могут отсутствовать. Если элемент не найден, возвращает null.
  3. findBy — для асинхронного поиска. Возвращает промис, который резолвится, когда элемент появляется в DOM, или падает с ошибкой после таймаута.
  4. getAllBy / queryAllBy / findAllBy — варианты поиска, возвращающие массив элементов, когда ожидается несколько совпадений.

Поиск по доступности: предпочтительный способ

React Testing Library рекомендует ориентироваться на доступность (Accessibility), а не на классы или id. Это повышает стабильность тестов и делает их ближе к пользовательскому сценарию. Основные методы:

  • getByRole(role, options) — поиск по роли ARIA, например button, heading, textbox. Опции позволяют уточнять имя или уровень заголовка.
  • getByLabelText(text, options) — поиск по label, связанному с input.
  • getByPlaceholderText(text) — поиск input по placeholder.
  • getByText(text, options) — поиск элементов по видимому тексту.
  • getByAltText(text) — поиск изображений по alt.
  • getByTitle(text) — поиск элементов по атрибуту title.
  • getByDisplayValue(value) — поиск элементов форм по текущему значению.

Пример:

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import LoginForm from './LoginForm';

render(<LoginForm />);
const usernameInput = screen.getByLabelText(/имя пользователя/i);
userEvent.type(usernameInput, 'testuser');
const submitButton = screen.getByRole('button', { name: /войти/i });
userEvent.click(submitButton);

В этом примере поиск выполняется по label и роль кнопки, что полностью соответствует пользовательскому сценарию.


Опции поиска

Многие методы getBy и их аналоги поддерживают дополнительные опции:

  • name — уточняет видимое имя элемента.
  • level — для заголовков (h1, h2 и т.д.).
  • exact — точное совпадение текста. По умолчанию поиск нечувствителен к регистру.
  • selector — дополнительный CSS-селектор внутри найденного элемента.

Пример:

screen.getByRole('heading', { level: 2, name: /профиль пользователя/i });

Асинхронные queries

Для компонентов, которые загружают данные или рендерят контент с задержкой, используют findBy. Этот метод возвращает промис, что позволяет работать с async/await.

const userName = await screen.findByText(/загрузка завершена/i);
expect(userName).toBeInTheDocument();

Асинхронные queries применяются чаще всего при тестировании запросов к API, динамических списков и эффектов, основанных на useEffect.


Различия getBy и queryBy

  • getBy — выброс ошибки при отсутствии элемента. Используется для строгих проверок.
  • queryBy — возвращает null, если элемент отсутствует. Используется для проверки отсутствия элемента:
expect(screen.queryByText(/ошибка/i)).not.toBeInTheDocument();

Использование queryBy повышает гибкость теста при проверке условного рендера.


getAllBy / queryAllBy / findAllBy

Когда ожидается несколько элементов, применяются методы с AllBy. Они возвращают массив.

const items = screen.getAllByRole('listitem');
expect(items).toHaveLength(3);

Если ни один элемент не найден, getAllBy выбрасывает ошибку, а queryAllBy возвращает пустой массив.


Советы по написанию стабильных queries

  1. Ставить на первое место доступность: getByRole, getByLabelText.
  2. Избегать поиска по классам, id или тестовым атрибутам, если есть доступные ARIA-атрибуты.
  3. Использовать асинхронные queries для динамического контента.
  4. Всегда уточнять имя или текст, чтобы избежать случайных совпадений.
  5. Предпочитать getBy / findBy для уверенности, queryBy — только для проверки отсутствия.

Итоговая структура поиска

  1. Строгие проверки присутствия: getByRole, getByLabelText, getByText.
  2. Проверка отсутствия: queryByText, queryByRole.
  3. Асинхронный поиск: findByText, findByRole.
  4. Множественные элементы: getAllBy*, queryAllBy*, findAllBy*.

Эта система queries позволяет писать тесты, максимально приближенные к поведению пользователя, и обеспечивает стабильность даже при изменении структуры DOM или CSS.