testing-msw

CoderMariusz's avatarfrom CoderMariusz

Apply when mocking API calls in tests or development: intercepting requests, simulating error states, and testing loading states.

⭐1stars🔀0forks📁View on GitHub🕐Updated Jan 4, 2026

When & Why to Use This Skill

This Claude skill facilitates seamless API mocking using Mock Service Worker (MSW) for both testing and development environments. It enables developers to intercept network requests at the network level, allowing for the simulation of realistic server behaviors including successful data retrieval, error states, and network latency without modifying application code.

Use Cases

  • Automated Integration Testing: Mocking backend responses to test frontend components and logic in isolation, ensuring consistent test results without a live server.
  • Error Handling Validation: Simulating 500 Internal Server Errors or 403 Forbidden responses to verify that the UI displays appropriate error messages and recovery options.
  • UX Performance Testing: Using artificial delays to simulate slow network conditions, allowing developers to test and refine loading spinners and skeleton screens.
  • Frontend-First Development: Intercepting browser requests during local development to build and preview features before the actual backend API endpoints are implemented.
  • Request Payload Assertion: Capturing and inspecting outgoing request bodies in tests to ensure that forms and API clients are sending the correct data structures to the server.
nametesting-msw
descriptionApply when mocking API calls in tests or development: intercepting requests, simulating error states, and testing loading states.
version1.0.0
tokens~600
confidencehigh
- https//mswjs.io/docs/best-practices/typescript
last_validated2025-01-10
next_review2025-01-24
tags[testing, msw, mocking, api]

When to Use

Apply when mocking API calls in tests or development: intercepting requests, simulating error states, and testing loading states.

Patterns

Pattern 1: Setup Handlers

// Source: https://mswjs.io/docs/getting-started
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: '1', name: 'John' },
      { id: '2', name: 'Jane' },
    ]);
  }),

  http.post('/api/users', async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json({ id: '3', ...body }, { status: 201 });
  }),
];

Pattern 2: Test Setup

// Source: https://mswjs.io/docs/getting-started
// src/mocks/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';

export const server = setupServer(...handlers);

// jest.setup.ts or vitest.setup.ts
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

Pattern 3: Test-Specific Overrides

// Source: https://mswjs.io/docs/best-practices/typescript
import { http, HttpResponse } from 'msw';
import { server } from './mocks/server';

test('handles server error', async () => {
  // Override for this test only
  server.use(
    http.get('/api/users', () => {
      return HttpResponse.json(
        { error: 'Server error' },
        { status: 500 }
      );
    })
  );

  render(<UserList />);
  expect(await screen.findByText(/error/i)).toBeInTheDocument();
});

test('handles empty list', async () => {
  server.use(
    http.get('/api/users', () => {
      return HttpResponse.json([]);
    })
  );

  render(<UserList />);
  expect(await screen.findByText(/no users/i)).toBeInTheDocument();
});

Pattern 4: Request Assertions

// Source: https://mswjs.io/docs/best-practices/typescript
test('sends correct data', async () => {
  let capturedBody: unknown;

  server.use(
    http.post('/api/users', async ({ request }) => {
      capturedBody = await request.json();
      return HttpResponse.json({ id: '1' }, { status: 201 });
    })
  );

  render(<CreateUserForm />);
  await userEvent.type(screen.getByLabelText(/name/i), 'John');
  await userEvent.click(screen.getByRole('button', { name: /submit/i }));

  await waitFor(() => {
    expect(capturedBody).toEqual({ name: 'John' });
  });
});

Pattern 5: Delayed Responses (Loading States)

// Source: https://mswjs.io/docs/api/delay
import { http, HttpResponse, delay } from 'msw';

server.use(
  http.get('/api/users', async () => {
    await delay(100); // Simulate network delay
    return HttpResponse.json([{ id: '1', name: 'John' }]);
  })
);

test('shows loading state', async () => {
  render(<UserList />);
  expect(screen.getByText(/loading/i)).toBeInTheDocument();
  expect(await screen.findByText('John')).toBeInTheDocument();
});

Pattern 6: Browser Setup (Development)

// Source: https://mswjs.io/docs/getting-started
// src/mocks/browser.ts
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';

export const worker = setupWorker(...handlers);

// main.tsx (development only)
if (process.env.NODE_ENV === 'development') {
  const { worker } = await import('./mocks/browser');
  await worker.start();
}

Anti-Patterns

  • Not resetting handlers - Always resetHandlers in afterEach
  • Global mocks in tests - Use server.use() for test-specific
  • No error scenarios - Test 4xx and 5xx responses
  • Mocking too much - Integration tests should hit real APIs

Verification Checklist

  • Server setup in test config (beforeAll/afterAll)
  • Handlers reset after each test
  • Error states tested with overrides
  • Loading states tested with delay()
  • Request body assertions where needed