Асинхронные операции играют ключевую роль в современных веб-приложениях. Взаимодействие с сервером, загрузка данных, выполнение долгих вычислений — всё это требует асинхронных подходов. Важно, чтобы тестирование таких операций не только проверяло правильность их выполнения, но и учитывало время, которое может понадобиться на завершение этих операций. Jest предоставляет мощные инструменты для асинхронного тестирования, и в этой главе рассмотрим, как эффективно использовать их при тестировании компонентов.
Для того чтобы понять, как работать с асинхронностью в Jest, стоит
разобраться с основными механизмами работы с асинхронным кодом:
Promise, async/await, а также функциями,
такими как setTimeout, setInterval, которые
могут использоваться в тестах.
Jest поддерживает асинхронные тесты с использованием трех основных подходов:
Promise, Jest будет ожидать его завершения.async/await: Для
асинхронных тестов можно использовать синтаксис async для
объявления асинхронной функции и await для ожидания
выполнения операции.done: Это старый
способ асинхронного тестирования, но он все еще используется в некоторых
проектах.Компоненты React, например, могут выполнять асинхронные запросы на сервер, передавать данные в состояние или выполнять другие длительные операции. Чтобы правильно протестировать такие компоненты, необходимо убедиться, что тесты не завершатся до того, как асинхронные действия будут завершены.
async/awaitОдин из самых популярных способов работы с асинхронными операциями в
Jest — это использование async/await. Это позволяет писать
тесты, которые выглядят как синхронный код, но при этом поддерживают
асинхронность.
test('компонент загружает данные асинхронно', async () => {
const { getByText } = render(<MyComponent />);
// Ожидаем появления текста после загрузки данных
await waitFor(() => getByText('Данные загружены'));
});
Здесь waitFor позволяет дождаться, когда ожидаемое
условие станет истинным (например, появление текста на экране).
await делает тест асинхронным, а waitFor
следит за тем, чтобы тест не завершился до того, как все асинхронные
действия в компоненте будут завершены.
PromiseЕсли компоненты работают с промисами напрямую (например, через
fetch или другие API, которые возвращают промис), то можно
просто вернуть промис в тесте. Jest сам позаботится о том, чтобы
дождаться его завершения.
test('компонент выполняет запрос и отображает результат', () => {
const { getByText } = render(<MyComponent />);
return waitFor(() => getByText('Данные загружены'));
});
В этом примере промис, возвращаемый waitFor, будет
автоматически ждать, пока не появится нужный элемент. Тест не
завершится, пока промис не будет выполнен.
doneИспользование done часто встречается в старых тестах или
в более сложных случаях, когда нужно вручную контролировать завершение
асинхронной операции. done передается в тестовую функцию и
вызывается, когда асинхронный код завершен.
test('компонент загружает данные с использованием done', (done) => {
const { getByText } = render(<MyComponent />);
setTimeout(() => {
expect(getByText('Данные загружены')).toBeInTheDocument();
done(); // Завершаем тест вручную
}, 1000);
});
Этот подход реже используется в современных тестах, так как
async/await значительно упрощает работу с асинхронными
тестами.
waitFor, findBy и других методовДля того чтобы тестировать компоненты, выполняющие асинхронные операции, важно правильно использовать утилиты, предоставляемые Jest и Testing Library.
waitForМетод waitFor используется для ожидания изменения
состояния компонента. Он полезен, если компонент изменяет своё состояние
или DOM после выполнения асинхронной операции. Например, при загрузке
данных с сервера:
test('компонент обновляет состояние после асинхронного запроса', async () => {
const { getByText } = render(<MyComponent />);
await waitFor(() => getByText('Данные загружены'));
});
Этот метод будет ждать, пока не произойдут изменения, и позволит тесту продолжиться только после того, как нужные данные окажутся в DOM.
findByВместо getByText можно использовать методы
findBy, которые ожидают появления элемента. Это идеальный
вариант для асинхронных тестов, где нужно дождаться появления элемента в
DOM.
test('компонент загружает данные и отображает их', async () => {
const { findByText } = render(<MyComponent />);
const element = await findByText('Данные загружены');
expect(element).toBeInTheDocument();
});
Метод findByText автоматически ожидает появления
элемента, что делает его удобным для работы с асинхронными
операциями.
Часто необходимо мокировать асинхронные вызовы, чтобы изолировать тестируемый компонент от внешних зависимостей. Jest предоставляет мощные инструменты для мокирования функций, включая асинхронные вызовы.
jest.mock('axios');
test('компонент загружает данные с сервера', async () => {
axios.get.mockResolvedValue({ data: 'Данные загружены' });
const { getByText } = render(<MyComponent />);
await waitFor(() => getByText('Данные загружены'));
});
В этом примере мы мокируем вызов axios.get, чтобы он
всегда возвращал промис с нужными данными, и таким образом тестируем,
как компонент реагирует на этот ответ.
Важной частью тестирования асинхронных операций является обработка ошибок. Необходимо убедиться, что компонент правильно обрабатывает ошибки, которые могут возникать при выполнении асинхронных операций.
test('компонент корректно обрабатывает ошибку загрузки данных', async () => {
axios.get.mockRejectedValue(new Error('Ошибка загрузки'));
const { getByText } = render(<MyComponent />);
await waitFor(() => getByText('Ошибка загрузки'));
});
Здесь мы мокируем ошибку, которая будет выброшена в ответ на запрос, и проверяем, что компонент корректно отобразит сообщение об ошибке.
Асинхронное тестирование компонентов в Jest требует внимательного
подхода к ожиданиям и контролю за временем выполнения асинхронных
операций. Использование таких методов, как async/await,
waitFor, findBy и мокирование асинхронных
вызовов позволяет эффективно тестировать поведение компонентов,
взаимодействующих с асинхронными источниками данных. Важно помнить, что
асинхронные операции должны быть правильно синхронизированы с тестами,
чтобы избежать ложных срабатываний или пропущенных ошибок.