findBy методы: асинхронный поиск элементов

React Testing Library (RTL) предоставляет разнообразные методы для поиска элементов в DOM, и среди них особое место занимают асинхронные методы поиска. Они реализуются через префикс findBy и служат для сценариев, когда элементы появляются в DOM с задержкой, например, после выполнения асинхронного запроса или обновления состояния.


Принцип работы findBy

Методы findBy являются асинхронными и возвращают промис, который разрешается в элемент, как только он появляется в DOM. Если элемент не найден за установленный таймаут (по умолчанию 1000 мс), промис отклоняется с ошибкой.

Синтаксис:

const element = await screen.findByRole('button', { name: /submit/i });
  • screen — стандартный объект RTL для поиска элементов.
  • findByRole — ищет элемент по роли (role) и возвращает промис.
  • Второй аргумент — опции поиска, например, name, exact или hidden.

Основные методы findBy

React Testing Library предоставляет полный набор findBy методов, аналогичный синхронным getBy:

  • findByText — ищет по текстовому содержимому.
  • findByRole — ищет по роли элемента (рекомендованный способ для доступности).
  • findByLabelText — ищет по связанным с элементом <label>.
  • findByPlaceholderText — ищет по атрибуту placeholder.
  • findByAltText — ищет по альтернативному тексту изображения.
  • findByTitle — ищет по атрибуту title.
  • findByTestId — ищет по data-testid (используется в крайнем случае).

Каждый метод возвращает промис, что позволяет использовать async/await или .then().


Отличие от getBy и queryBy

Метод Возвращаемое значение Поведение при отсутствии элемента
getBy... Элемент DOM Бросает ошибку сразу
queryBy... Элемент DOM или null Возвращает null, ошибок нет
findBy... Промис, который резолвится в элемент Асинхронно ждет появления, бросает ошибку по таймауту

Использование findBy оправдано, когда элемент не появляется мгновенно после рендера, например, при:

  • Получении данных с сервера.
  • Анимации или задержках в отображении компонента.
  • Отложенной генерации элементов через состояние.

Пример с асинхронными данными

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

test('кнопка появляется после запроса', async () => {
  render(<AsyncButton />);
  
  // Кликаем, чтобы инициировать асинхронное действие
  userEvent.click(screen.getByText(/загрузить/i));
  
  // Асинхронно ожидаем появления кнопки
  const button = await screen.findByRole('button', { name: /подтвердить/i });
  
  expect(button).toBeInTheDocument();
});

В этом примере:

  1. Пользователь кликает по кнопке, что запускает асинхронную операцию.
  2. findByRole ждет, пока кнопка с текстом “Подтвердить” появится в DOM.
  3. Проверка toBeInTheDocument() выполняется только после того, как элемент найден.

Опции поиска и таймаут

Методы findBy поддерживают настройку таймаута и интервала повторного поиска через объект options:

const element = await screen.findByText('Загрузка завершена', {
  exact: true,
  timeout: 2000 // ждать до 2 секунд
});
  • timeout — максимальное время ожидания в миллисекундах.
  • exact — учитывать точное совпадение текста (true по умолчанию).

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


Работа с коллекциями элементов: findAllBy

Для поиска нескольких элементов асинхронно используется findAllBy:

const items = await screen.findAllByRole('listitem');
expect(items).toHaveLength(3);
  • Возвращает массив элементов, как только все они появились в DOM.
  • Если хотя бы один элемент не найден за таймаут, промис отклоняется.

Ошибки и рекомендации

  • Использование findBy оправдано только при асинхронном рендере. Для синхронных элементов лучше использовать getBy.
  • В тестах с findBy всегда применять await, иначе проверка не дождется элемента.
  • Комбинирование с waitFor допустимо, но чаще findBy полностью заменяет необходимость в waitFor.

Взаимодействие с событиями

Асинхронный поиск особенно полезен после событий, которые запускают обновление DOM:

userEvent.click(screen.getByText('Загрузить'));

const message = await screen.findByText('Данные загружены');
expect(message).toBeVisible();
  • findBy автоматически повторяет поиск с небольшими интервалами.
  • Элемент может появиться после сетевого запроса, таймера или анимации, и тест не завершится преждевременно.

Итоговое применение

Методы findBy и findAllBy в React Testing Library позволяют:

  • Ждать элементов, которые появляются не сразу после рендера.
  • Писать стабильные тесты для асинхронных интерфейсов.
  • Сохранять семантическую проверку через role, label и текст, повышая доступность.

Использование findBy — это ключ к надежным тестам в современном React-приложении, где асинхронные операции и динамическое отображение контента стали нормой.