Tooling6 min read

API Mocking & Contract Testing

Mock APIs at the network level with MSW, generate contract tests with Pact, and create realistic test fixtures for reliable front-end development.

mockingmswtestingapi

API mocking decouples frontend development and testing from backend availability. MSW (Mock Service Worker) intercepts requests at the network layer - the same mock code works in tests, Storybook, and the browser, with no changes to application code.

Use MSW as your primary mocking tool - it intercepts at the network level so your app code is identical in test and production. Use Pact for consumer-driven contracts to detect API breakage without end-to-end tests. Generate realistic data with faker.js factories.

Workflow

  1. Set up MSW for browser and Node environmentsOne handler definition works in Jest, Vitest, Storybook, and the browser - DRY mocking.
  2. Write realistic handlers with request inspectionHandlers that always return the same response don't test conditional logic.
  3. Build fixture factories with faker.jsHardcoded test data misses edge cases; faker generates realistic, varied data automatically.
  4. Implement Pact consumer-driven contract testsTraditional API tests break silently when the API changes. Pact publishes contracts that providers must verify - catching breaking changes before deployment.
  5. Use MSW in Storybook for UI developmentStories with real-looking data from MSW enable UI development without a running backend.

Output Format

MSW handlers covering all API endpoints, Pact consumer tests for critical integrations, faker.js factories for test data, Storybook stories with MSW integration.

Examples

MSW handler: `http.post("/api/login", async ({ request }) => { const { email } = await request.json(); return email === "admin@test.com" ? HttpResponse.json({ token: "fake-jwt" }) : HttpResponse.json({ error: "Invalid credentials" }, { status: 401 }) })`
Faker factory: `const createProduct = (overrides = {}) => ({ id: faker.string.uuid(), name: faker.commerce.productName(), price: parseFloat(faker.commerce.price({ min: 5, max: 500 })), ...overrides })`
Test override: `it("shows empty state", () => { server.use(http.get("/api/items", () => HttpResponse.json([]))); render(<ItemList />); expect(screen.getByText("No items found")).toBeInTheDocument(); })`

Edge Cases & Gotchas

  • MSW and third-party SDKs: SDKs that bypass fetch (e.g., WebSocket, XHR in legacy code) need additional MSW setup or different mocking strategies
  • Pact version compatibility: consumer and provider must agree on the pact specification version; enforce this in Pact Broker settings
  • Streaming APIs: MSW supports ReadableStream for streaming responses; test SSE and streaming fetch with `new ReadableStream(...)` in HttpResponse

When to Break These Rules

  • For pure unit tests of isolated functions that don't make HTTP calls, MSW adds unnecessary setup complexity - mock at the module level instead.

Reference Files

  • MSW docs: https://mswjs.io/
  • Pact Foundation: https://docs.pact.io/
  • Faker.js: https://fakerjs.dev/
  • MSW Storybook addon: https://storybook.js.org/addons/msw-storybook-addon

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.