В тестировании React-компонентов часто возникает необходимость проверять поведение, связанное с асинхронными операциями: сетевыми запросами, таймерами или динамическим обновлением состояния. React Testing Library предоставляет инструменты для корректного взаимодействия с асинхронным кодом и гарантирует, что тесты будут стабильными и предсказуемыми.
Асинхронные операции в компонентах React обычно реализуются через:
fetch или axios для получения данных с сервера;setTimeout, setInterval);При тестировании важно дождаться завершения асинхронного действия,
чтобы проверка отражала реальное состояние компонента. Простое
использование expect сразу после рендера часто приводит к
ошибкам, так как DOM ещё не обновился.
1. waitFor
waitFor — универсальный инструмент для ожидания
изменения DOM. Он повторно выполняет переданную функцию до тех пор, пока
не выполнится условие или не истечёт тайм-аут.
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MyComponent from './MyComponent';
test('отображает данные после загрузки', async () => {
render(<MyComponent />);
userEvent.click(screen.getByText('Загрузить данные'));
await waitFor(() => expect(screen.getByText('Данные загружены')).toBeInTheDocument());
});
Особенности waitFor:
2. findBy и findAllBy
findBy* и findAllBy* — асинхронные версии
стандартных селекторов (getBy* и queryBy*).
Они автоматически ждут появления элемента в DOM.
test('загружает элемент асинхронно', async () => {
render(<MyComponent />);
const element = await screen.findByText('Асинхронный текст');
expect(element).toBeInTheDocument();
});
Преимущества:
При прямом вызове асинхронной функции можно использовать
async/await:
test('проверяет результат промиса', async () => {
const fetchData = jest.fn().mockResolvedValue('данные');
const result = await fetchData();
expect(result).toBe('данные');
});
Для компонентов с побочными эффектами (useEffect с
async вызовом) корректное тестирование включает ожидание
DOM-обновления:
import { render, screen } from '@testing-library/react';
import DataLoader from './DataLoader';
test('рендерит данные после fetch', async () => {
render(<DataLoader />);
const loadedData = await screen.findByText('Загруженные данные');
expect(loadedData).toBeVisible();
});
Важно помнить, что useEffect не блокирует рендер,
поэтому проверка должна использовать асинхронные методы
(waitFor или findBy).
При тестировании компонентов с setTimeout или
setInterval рекомендуется использовать фейковые таймеры
Jest:
jest.useFakeTimers();
test('ожидает таймер', () => {
const callback = jest.fn();
setTimeout(callback, 1000);
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalled();
});
Особенности:
jest.advanceTimersByTime(ms) ускоряет тестирование
асинхронного кода без реального ожидания.jest.useRealTimers().При тестировании ошибок асинхронных операций важно оборачивать
проверки в try/catch или использовать
expect(...).rejects:
test('обрабатывает ошибку fetch', async () => {
const fetchData = jest.fn().mockRejectedValue(new Error('Ошибка сети'));
await expect(fetchData()).rejects.toThrow('Ошибка сети');
});
Для компонентов с асинхронной ошибкой:
render(<MyComponent />);
await waitFor(() => expect(screen.getByText('Ошибка загрузки')).toBeInTheDocument());
waitFor гарантирует, что тест дождётся появления
элемента с сообщением об ошибке, даже если ошибка произойдет с
задержкой.
findBy для простых случаев появления
элемента.waitFor для комплексных проверок и
последовательных изменений DOM.setTimeout для ожидания вручную
(await new Promise(resolve => setTimeout(...))).jest.mock, jest.fn).afterEach для jest.clearAllMocks() и
jest.useRealTimers()).Асинхронность в React Testing Library строится вокруг корректного
ожидания изменений DOM, промисов и таймеров. Правильное использование
waitFor, findBy и моков позволяет создавать
стабильные и предсказуемые тесты, минимизируя ложные срабатывания и
гонки состояний.