Тестирование асинхронного интерфейса и вызовов API с MSW
Имитационно обрабатывайте HTTP-запросы с помощью Mock Service Worker, чтобы тестировать компоненты, получающие данные.
«Тестирование асинхронного интерфейса и вызовов API с MSW» — бесплатный урок React Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения React Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс React Academy содержит 4 уроков всего.
Зачем нужен Mock Service Worker
MSW перехватывает HTTP-запросы на уровне сети с помощью сервис-воркера в браузере или перехватчика http в Node, позволяя имитировать API без изменений в коде приложения.
Установка MSW
Установите MSW и настройте перехватчик Node для тестов Jest и Vitest.
// npm install msw --save-dev
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('/api/users', () => {
return HttpResponse.json([{ id: 1, name: 'Alice' }]);
}),
];Настройка имитационного сервера
Создайте экземпляр setupServer со своими обработчиками и вызывайте server.listen() перед тестами, server.resetHandlers() после каждого теста и server.close() после всех тестов.
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
const server = setupServer(...handlers);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());Проверка компонента, выполняющего запросы
Отобразите компонент, который выполняет запрос. MSW перехватит запрос и вернёт имитационные данные. Проверьте, что видит пользователь после загрузки данных.
test('shows user list after fetch', async () => {
render(<UserList />);
expect(screen.getByText('Loading...')).toBeInTheDocument();
expect(await screen.findByText('Alice')).toBeInTheDocument();
});Запросы findBy для асинхронного интерфейса
Запросы findBy возвращают обещание, которое выполняется при появлении элемента, — по умолчанию в течение тайм-аута до 1 секунды. Используйте их для содержимого, появляющегося после асинхронных операций.
// findByText waits for the element to appear
const username = await screen.findByText('Alice');
expect(username).toBeInTheDocument();
// waitFor wraps assertions that need to wait
await waitFor(() => {
expect(screen.getByText('3 users')).toBeInTheDocument();
});Переопределение обработчиков для отдельного теста
Используйте server.use() внутри теста, чтобы добавить одноразовый обработчик, переопределяющий стандартный только для этого теста.
test('shows error on API failure', async () => {
server.use(
http.get('/api/users', () => {
return HttpResponse.json({ message: 'Server error' }, { status: 500 });
})
);
render(<UserList />);
expect(await screen.findByText('Failed to load users')).toBeInTheDocument();
});Проверка состояний загрузки
Проверьте индикатор загрузки сразу после рендеринга, синхронно, до поступления асинхронных данных.
test('shows loading state initially', () => {
render(<UserList />);
expect(screen.getByRole('progressbar')).toBeInTheDocument();
// or
expect(screen.getByText('Loading...')).toBeInTheDocument();
});Тестирование запросов POST
Определите обработчик POST, который проверяет тело запроса и возвращает созданный ресурс. Проверьте, что компонент отражает состояние успешного выполнения.
server.use(
http.post('/api/users', async ({ request }) => {
const body = await request.json();
return HttpResponse.json({ id: 2, ...body }, { status: 201 });
})
);
test('creates a user', async () => {
const user = userEvent.setup();
render(<NewUserForm />);
await user.type(screen.getByLabelText('Name'), 'Bob');
await user.click(screen.getByRole('button', { name: /create/i }));
expect(await screen.findByText('User created!')).toBeInTheDocument();
});Задержки сети для реалистичных тестов
Добавьте к обработчикам искусственные задержки, чтобы тщательнее проверять интерфейс загрузки, используя утилиту delay MSW.
import { delay } from 'msw';
http.get('/api/users', async () => {
await delay(200);
return HttpResponse.json([{ id: 1, name: 'Alice' }]);
})Тестирование с React Query
Оборачивайте компонент в новый QueryClientProvider для каждого теста, чтобы состояние не переходило между тестами.
function renderWithQuery(ui: React.ReactElement) {
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
return render(
<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>
);
}Проверка данных запроса
Используйте шпион запросов внутри обработчика, чтобы проверить, что приложение отправило правильное тело запроса или заголовки.
let capturedBody: unknown;
server.use(
http.post('/api/login', async ({ request }) => {
capturedBody = await request.json();
return HttpResponse.json({ token: 'abc' });
})
);
test('sends credentials to API', async () => {
// ... submit form ...
expect(capturedBody).toEqual({ email: 'a@b.com', password: 'pass' });
});MSW в браузере при разработке
MSW также работает в браузере во время разработки. Выполните npx msw init public/, чтобы установить сервис-воркер, и импортируйте обработчики в main.tsx, чтобы использовать имитационный API при локальной разработке.
Быстрая проверка
Какой тип запроса RTL следует использовать, чтобы проверить содержимое, появляющееся после завершения вызова API?
Итоги
MSW перехватывает реальные HTTP-запросы, поэтому тесты остаются реалистичными и не обращаются к работающему серверу. Используйте server.use(), чтобы переопределять обработчики для отдельных тестов, findBy для асинхронных проверок и оборачивайте тесты React Query в новый QueryClientProvider.
Часто задаваемые вопросы
Урок «Тестирование асинхронного интерфейса и вызовов API с MSW» бесплатный?
Да — полный текст урока «Тестирование асинхронного интерфейса и вызовов API с MSW» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс React Academy, подпишись на CoddyKit PRO. Курс React Academy содержит 4 уроков всего.
Чему я научусь в уроке «Тестирование асинхронного интерфейса и вызовов API с MSW»?
Имитационно обрабатывайте HTTP-запросы с помощью Mock Service Worker, чтобы тестировать компоненты, получающие данные. Ты практикуешь React Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать React Academy?
Предыдущий опыт не требуется. React Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Тестирование асинхронного интерфейса и вызовов API с MSW»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке React Academy?
Да. Каждый урок React Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Интеграционное тестирование с React Testing Library
- Тестирование асинхронного интерфейса и вызовов API с MSW
- Начало работы с Playwright для React
- Сквозное тестирование форм и пользовательских сценариев