Асинхронное тестирование компонентов

Асинхронные операции играют ключевую роль в современных веб-приложениях. Взаимодействие с сервером, загрузка данных, выполнение долгих вычислений — всё это требует асинхронных подходов. Важно, чтобы тестирование таких операций не только проверяло правильность их выполнения, но и учитывало время, которое может понадобиться на завершение этих операций. Jest предоставляет мощные инструменты для асинхронного тестирования, и в этой главе рассмотрим, как эффективно использовать их при тестировании компонентов.

Основы асинхронных тестов в Jest

Для того чтобы понять, как работать с асинхронностью в Jest, стоит разобраться с основными механизмами работы с асинхронным кодом: Promise, async/await, а также функциями, такими как setTimeout, setInterval, которые могут использоваться в тестах.

Jest поддерживает асинхронные тесты с использованием трех основных подходов:

  1. Возвращение Promise: Если тест возвращает Promise, Jest будет ожидать его завершения.
  2. Использование async/await: Для асинхронных тестов можно использовать синтаксис async для объявления асинхронной функции и await для ожидания выполнения операции.
  3. Использование функции done: Это старый способ асинхронного тестирования, но он все еще используется в некоторых проектах.

Тестирование компонентов с асинхронными операциями

Компоненты React, например, могут выполнять асинхронные запросы на сервер, передавать данные в состояние или выполнять другие длительные операции. Чтобы правильно протестировать такие компоненты, необходимо убедиться, что тесты не завершатся до того, как асинхронные действия будут завершены.

1. Тестирование с использованием async/await

Один из самых популярных способов работы с асинхронными операциями в Jest — это использование async/await. Это позволяет писать тесты, которые выглядят как синхронный код, но при этом поддерживают асинхронность.

test('компонент загружает данные асинхронно', async () => {
  const { getByText } = render(<MyComponent />);
  
  // Ожидаем появления текста после загрузки данных
  await waitFor(() => getByText('Данные загружены'));
});

Здесь waitFor позволяет дождаться, когда ожидаемое условие станет истинным (например, появление текста на экране). await делает тест асинхронным, а waitFor следит за тем, чтобы тест не завершился до того, как все асинхронные действия в компоненте будут завершены.

2. Тестирование с использованием Promise

Если компоненты работают с промисами напрямую (например, через fetch или другие API, которые возвращают промис), то можно просто вернуть промис в тесте. Jest сам позаботится о том, чтобы дождаться его завершения.

test('компонент выполняет запрос и отображает результат', () => {
  const { getByText } = render(<MyComponent />);
  
  return waitFor(() => getByText('Данные загружены'));
});

В этом примере промис, возвращаемый waitFor, будет автоматически ждать, пока не появится нужный элемент. Тест не завершится, пока промис не будет выполнен.

3. Тестирование с использованием функции 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 и мокирование асинхронных вызовов позволяет эффективно тестировать поведение компонентов, взаимодействующих с асинхронными источниками данных. Важно помнить, что асинхронные операции должны быть правильно синхронизированы с тестами, чтобы избежать ложных срабатываний или пропущенных ошибок.