Streamlining API Mocking and Testing with Mock Service Worker (MSW)
Introduction
This article explains “Mock Service Worker (MSW)”, a very useful tool for frontend development and testing. By using MSW, you can continue frontend development even when the backend APIs are not yet complete, and you can also improve the stability of your tests. We’ll first introduce the basic usage of MSW, then explain in detail how to leverage it, including a comparison with test code that does not use MSW.
Goal of this article
We will verify the effectiveness of MSW using automated tests for a React component that fetches data.
First, we’ll write test code that uses Jest mocks.
Then we’ll replace the Jest mocks with MSW and compare how this makes the test code easier to write.
We’ll also cover how to start the MSW server, so you can use this article as a reference to apply it in real projects.
What is Mock Service Worker (MSW)?
MSW (Mock Service Worker) is a library for mocking APIs (creating fake server responses).
It runs in both browser and Node.js environments, and its main feature is that it can return mock responses during frontend development and testing without calling the real APIs.
Main use cases
-
Continue frontend development even when the backend is not ready
Even if the real API is not yet available, you can return mock data and keep development moving.
If the API design changes, you can quickly update the mock data. -
Stabilize tests
By mocking APIs in tests with Jest / Playwright / Cypress, you can run stable tests without being affected by external APIs.
You can easily simulate network errors or specific responses. -
Hook into network requests to make debugging easier during development
With MSW, it’s easy to inspect what data is actually being sent and received in requests.
For example, you can customize API responses to check how your error handling behaves.
Testing a component that displays a list of articles
This time, we’ll prepare a component that displays a list of blog articles and consider how to implement its test code.
'use client'
import { useEffect, useState } from "react"
type Article = {
id: number,
title: string
description?: string
}
export const Articles = () => {
const [articles, setArticles] = useState<Article[] | null>(null)
useEffect(() => {
fetch('/api/articles')
.then((response) => response.json())
.then((data) => setArticles(data.message));
}, []);
return (
<div>
{articles?.length ? (
<ul>
{articles.map((item) =>
<li key={item.id}>{item.title}</li>
)}
</ul>
) : (
<p>No articles found</p>
)}
</div>
)
}
When the screen loads, the data-fetching process runs.
If data exists, it is stored with useState and the result is displayed; that’s what this component does.
We then use this component to render the screen.
import { Articles } from "@/components/articles";
export default function Home() {
return (
<div>
<Articles />
</div>
)
}
With this setup, you can check the article list component in the browser, so start Next.js once and verify it.
npm run dev
The screen shows “No articles found” (“No articles available”), and the console log shows a 404 Not Found error from fetch.
Implementing the test code
First, we’ll run tests using fake data by mocking fetch directly, without using Mock Service Worker.
import { render, screen, waitFor } from '@testing-library/react';
import { Articles } from './articles';
describe('Articles Component', () => {
beforeEach(() => {
global.fetch = jest.fn();
});
afterEach(() => {
jest.resetAllMocks();
});
test('Display blog article list', async () => {
const mockArticles = [
{ id: 1, title: 'First Article', description: 'This is a test.' },
{ id: 2, title: 'Second Article', description: 'This is also a test.' }
];
(global.fetch as jest.Mock).mockResolvedValueOnce({
json: jest.fn().mockResolvedValueOnce(mockArticles),
});
// Render component
render(<Articles />);
expect(screen.getByText('No articles found')).toBeInTheDocument();
await waitFor(() => {
const items = screen.getAllByRole('listitem');
expect(items).toHaveLength(2);
expect(screen.getByText('First Article')).toBeInTheDocument();
expect(screen.getByText('Second Article')).toBeInTheDocument();
});
});
});
Here is an explanation of the test code.
beforeEach(() => {
global.fetch = jest.fn();
});
global.fetch is the default fetch instance in a browser environment.
By mocking it with jest.fn(), you can define responses for each request using mockResolvedValueOnce or mockRejectedValueOnce.
const mockArticles = [
{ id: 1, title: 'First Article', description: 'This is a test.' },
{ id: 2, title: 'Second Article', description: 'This is also a test.' }
];
(global.fetch as jest.Mock).mockResolvedValueOnce({
json: jest.fn().mockResolvedValueOnce(mockArticles),
});
We use mockResolvedValueOnce so that the json function returns the expected data.
expect(screen.getByText('No articles found')).toBeInTheDocument();
At the moment the component starts up, there is no article data, so we verify that “No articles found” (“No articles available”) is displayed.
await waitFor(() => {
const items = screen.getAllByRole('listitem');
expect(items).toHaveLength(2);
expect(screen.getByText('First Article')).toBeInTheDocument();
expect(screen.getByText('Second Article')).toBeInTheDocument();
});
After the article data has been set, we verify the following two points:
- There are two
litags - Each article title is displayed
Now that the test code is ready, let’s actually run the test.
npm run test -- components/articles.test.tsx
Even without using Mock Service Worker, we were able to test the component by mocking fetch.
In many cases, this may be sufficient.
However, for those who want to simplify test implementation further and reduce effort, I’d like to introduce Mock Service Worker (MSW).
Setting up Mock Service Worker
Install msw in your project.
npm install msw --save-dev
Create MSW handlers
Create handlers that define mock responses for API requests.
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('/api/articles', () => {
return HttpResponse.json([
{ id: 1, title: 'First Article', description: 'This is a test.' },
{ id: 2, title: 'Second Article', description: 'This is also a test.' }
])
})
];
Configure the MSW server
Set up an MSW server for tests.
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
Start MSW in the Jest setup
Start the MSW server before tests begin and clean it up afterward.
import '@testing-library/jest-dom'
+ import { server } from './mocks/server';
+ beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
+ afterEach(() => server.resetHandlers());
+ afterAll(() => server.close());
Configure Jest to load jest.setup.ts.
If you’re using the same environment as in the previous article, this should already be configured and no changes are needed.
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
To run Node.js with Jest
In jest.config.ts, we’ve configured Jest assuming it runs in a browser-like environment.
const config: Config = {
clearMocks: true,
coverageProvider: "v8",
preset: "ts-jest",
testEnvironment: "jest-environment-jsdom",
Because of this, starting MSW from Jest with the current settings will cause an error.
Following the official documentation, we’ll introduce jest-fixed-jsdom.
npm i -D jest-fixed-jsdom
Update the settings in jest.config.ts.
const config: Config = {
clearMocks: true,
coverageProvider: "v8",
preset: "ts-jest",
- testEnvironment: "jest-environment-jsdom",
+ testEnvironment: "jest-fixed-jsdom",
Modifying the test code
Since MSW is running when Jest executes tests, the component can now use fetch as-is.
Remove the logic that mocked fetch.
import { render, screen, waitFor } from '@testing-library/react';
import { Articles } from './articles';
describe('Articles Component', () => {
- beforeEach(() => {
- global.fetch = jest.fn();
- });
- afterEach(() => {
- jest.resetAllMocks();
- });
test('Display blog article list', async () => {
- const mockArticles = [
- { id: 1, title: 'First Article', description: 'This is a test.' },
- { id: 2, title: 'Second Article', description: 'This is also a test.' }
- ];
- (global.fetch as jest.Mock).mockResolvedValueOnce({
- json: jest.fn().mockResolvedValueOnce(mockArticles),
- });
// Render component
render(<Articles />);
expect(screen.getByText('No articles found')).toBeInTheDocument();
await waitFor(() => {
const items = screen.getAllByRole('listitem');
expect(items).toHaveLength(2);
expect(screen.getByText('First Article')).toBeInTheDocument();
expect(screen.getByText('Second Article')).toBeInTheDocument();
});
});
});
With the mocks removed, the test code has become easier to read.
Run the test with the following command and confirm that it finishes successfully.
npm run test -- components/articles.test.tsx
Conclusion
Mock Service Worker (MSW) is a highly useful tool for improving the efficiency of frontend development and testing. By mocking real API requests, it reduces common issues that arise during development and increases the reliability of your tests. With MSW, you can continue frontend development even before the backend is complete, and you can run tests smoothly. Use this article as a reference to introduce MSW into your projects and further improve your development efficiency.
Questions about this article 📝
If you have any questions or feedback about the content, please feel free to contact us.Go to inquiry form
Related Articles
Implement E2E tests with Playwright to achieve user-centric testing including inter-system integration
2023/10/02Implementing Essential UI Component Tests for Frontend Development with React Testing Library
2023/09/20Test Strategy in the Next.js App Router Era: Development Confidence Backed by Jest, RTL, and Playwright
2025/04/22Complete Guide to Web Accessibility: From Automated Testing with Lighthouse / axe and Defining WCAG Criteria to Keyboard Operation and Screen Reader Support
2023/11/21How to Easily Build a Web API with Express and MongoDB [TypeScript Compatible]
2024/12/09Express (+ TypeScript) Beginner’s Guide: How to Quickly Build Web Applications
2024/12/07Complete Guide to Refactoring React: Improve Your Code with Modularization, Render Optimization, and Design Patterns
2025/01/13Test Automation with Jest and TypeScript: A Complete Guide from Basic Setup to Writing Type-Safe Tests
2023/09/13

