0Pricing
React Academy · Lesson

Testing Async UI & API Calls with MSW

Mock HTTP requests with Mock Service Worker to test components that fetch data.

Testing Async UI & API Calls with MSW is a free React Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Mock Service Worker?

MSW intercepts HTTP requests at the network level using a Service Worker (browser) or http interceptor (Node), letting you mock APIs without touching your application code.

Installing MSW

Install MSW and set up the Node interceptor for Jest/Vitest tests.

// 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' }]);
  }),
];

Setting Up the Mock Server

Create a setupServer instance with your handlers and call server.listen() before tests, server.resetHandlers() after each, and server.close() after all.

import { setupServer } from 'msw/node';
import { handlers } from './handlers';

const server = setupServer(...handlers);

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

Testing a Component That Fetches

Render the component that triggers a fetch. MSW intercepts the request and returns mock data. Assert on what the user sees after the data loads.

test('shows user list after fetch', async () => {
  render(<UserList />);
  expect(screen.getByText('Loading...')).toBeInTheDocument();
  expect(await screen.findByText('Alice')).toBeInTheDocument();
});

findBy Queries for Async UI

findBy queries return a promise that resolves when the element appears (up to a 1-second timeout by default). Use them for content that appears after async operations.

// 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();
});

Overriding Handlers Per Test

Use server.use() inside a test to add a one-time handler that overrides the default for that test only.

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();
});

Testing Loading States

Assert on the loading indicator immediately after render (synchronously), before the async data arrives.

test('shows loading state initially', () => {
  render(<UserList />);
  expect(screen.getByRole('progressbar')).toBeInTheDocument();
  // or
  expect(screen.getByText('Loading...')).toBeInTheDocument();
});

Testing POST Requests

Define a POST handler that validates the request body and returns a created resource. Assert the component reflects the success state.

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();
});

Network Delays for Realistic Tests

Add artificial delays to handlers to test loading UI more thoroughly, using MSW's delay utility.

import { delay } from 'msw';

http.get('/api/users', async () => {
  await delay(200);
  return HttpResponse.json([{ id: 1, name: 'Alice' }]);
})

Testing with React Query

Wrap the component in a fresh QueryClientProvider for each test to prevent state leaking between tests.

function renderWithQuery(ui: React.ReactElement) {
  const queryClient = new QueryClient({
    defaultOptions: { queries: { retry: false } },
  });
  return render(
    <QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>
  );
}

Asserting Request Data

Use a request spy inside a handler to verify the application sent the correct request body or headers.

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 in the Browser for Dev

MSW also works in the browser during development. Run npx msw init public/ to install the service worker and import your handlers in main.tsx for a mock API during local dev.

Quick Check

Which RTL query type should you use to assert on content that appears after an API call resolves?

Recap

MSW intercepts real HTTP requests, making tests realistic without hitting a live server. Use server.use() to override handlers per test, findBy for async assertions, and wrap React Query tests in a fresh QueryClientProvider.

Frequently asked questions

Is the “Testing Async UI & API Calls with MSW” lesson free?

Yes — the full text of “Testing Async UI & API Calls with MSW” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.

What will I learn in “Testing Async UI & API Calls with MSW”?

Mock HTTP requests with Mock Service Worker to test components that fetch data. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start React Academy?

No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Testing Async UI & API Calls with MSW” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this React Academy lesson?

Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Integration Testing with React Testing Library
  2. Testing Async UI & API Calls with MSW
  3. Getting Started with Playwright for React
  4. Testing Forms & User Flows End-to-End
← Back to React Academy