Задержки ответов: симуляция медленной сети

Тестирование компонентов, работающих с асинхронными запросами, требует моделирования реальных условий работы сети. Одним из важных аспектов является задержка ответа сервера. Без корректной симуляции медленной сети тесты могут не выявить проблемы с отображением состояния загрузки, обработкой ошибок или некорректным обновлением интерфейса.


Использование jest.fn() и mockImplementation

Для контроля задержек в асинхронных функциях удобно использовать мок-функции. Например, при тестировании компонента, который делает запрос через fetch:

global.fetch = jest.fn();

beforeEach(() => {
  fetch.mockClear();
});

test('отображает индикатор загрузки при медленном ответе', async () => {
  fetch.mockImplementation(() =>
    new Promise(resolve => 
      setTimeout(() => resolve({ json: () => Promise.resolve({ data: 'ok' }) }), 1000)
    )
  );

  render(<MyComponent />);

  expect(screen.getByText(/загрузка/i)).toBeInTheDocument();

  await waitFor(() => expect(screen.getByText(/ok/i)).toBeInTheDocument());
});

Ключевые моменты:

  • mockImplementation позволяет задать любую асинхронную логику.
  • setTimeout эмулирует задержку сети.
  • waitFor гарантирует корректное ожидание обновления DOM после асинхронной операции.

Применение msw для более реалистичного мокирования API

Библиотека Mock Service Worker (msw) предоставляет гибкий способ эмуляции сетевых запросов, включая задержки, ошибки и динамические ответы.

import { rest } from 'msw';
import { setupServer } from 'msw/node';

const server = setupServer(
  rest.get('/api/data', (req, res, ctx) => {
    return res(
      ctx.delay(1500), // задержка 1.5 секунды
      ctx.json({ data: 'response from server' })
    );
  })
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test('проверяет работу компонента с медленной сетью', async () => {
  render(<MyComponent />);

  expect(screen.getByText(/загрузка/i)).toBeInTheDocument();

  const item = await screen.findByText(/response from server/i);
  expect(item).toBeInTheDocument();
});

Преимущества msw:

  • Позволяет полностью изолировать тесты от реального API.
  • ctx.delay() симулирует любую длительность ответа.
  • Легко комбинируется с различными сценариями ошибок, пустых ответов и перезапросов.

Проверка состояния загрузки и ошибок

Эмуляция медленной сети позволяет тестировать следующие состояния:

  1. Индикатор загрузки Компонент должен отображать спиннер или текст «Загрузка» до получения ответа.

  2. Обработка ошибок С помощью мока можно возвращать ошибки после задержки и проверять, что компонент корректно отображает сообщение об ошибке.

fetch.mockImplementation(() =>
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Сбой сети')), 500)
  )
);

render(<MyComponent />);
expect(screen.getByText(/загрузка/i)).toBeInTheDocument();
await waitFor(() => expect(screen.getByText(/сбой сети/i)).toBeInTheDocument());
  1. Повторные запросы Тестирование медленной сети позволяет убедиться, что компонент корректно обрабатывает повторные запросы или отмену предыдущих.

Использование асинхронных утилит React Testing Library

findBy* и waitFor — основные инструменты для работы с компонентами, зависящими от асинхронных данных.

  • findBy* автоматически ждёт появления элемента в DOM с таймаутом по умолчанию 1000 мс.
  • waitFor позволяет задавать свои условия ожидания и таймауты:
await waitFor(() => {
  expect(screen.getByText(/данные загружены/i)).toBeInTheDocument();
}, { timeout: 2000 });

Это особенно важно при тестировании компонентов, которые показывают прогресс загрузки или используют анимации.


Советы по эффективному тестированию задержек

  • Минимизировать реальные таймауты в тестах: для ускорения выполнения можно уменьшить setTimeout или ctx.delay, сохраняя относительное поведение.
  • Явно проверять состояние loading, error и success, чтобы тесты не зависели только от конечного результата.
  • Использовать изолированные мок-сервисы вместо реального API для стабильности и повторяемости тестов.
  • В случае сложных цепочек асинхронных вызовов комбинировать waitFor с findBy*, чтобы корректно обрабатывать все этапы загрузки.

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