How to Mock Fetch API in React
This article provides a straightforward guide on how to mock the Fetch API in React applications for unit testing. You will learn how to mock the global fetch object using Jest spy functions, as well as how to use Mock Service Worker (MSW) for a more robust and realistic API mocking solution. By the end of this guide, you will be able to write clean, isolated tests for your React components without making actual network requests.
Why Mock the Fetch API?
When testing React components, you should avoid making real HTTP requests. Real network requests make tests slow, fragile, and dependent on external servers. Mocking the Fetch API allows you to: * Control the API response data and status codes (e.g., 200 OK, 404 Not Found, 500 Server Error). * Test how your UI handles loading and error states. * Ensure tests run quickly and consistently in offline environments.
Method 1: Mocking Global Fetch with Jest
The simplest way to mock fetch is by overriding the
global fetch object provided by the browser environment in
your Jest tests.
The Component to Test
Here is a simple React component that fetches and displays a user’s name:
import React, { useState, useEffect } from 'react';
export default function UserProfile() {
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://api.example.com/user')
.then((res) => {
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
})
.then((data) => setUser(data))
.catch((err) => setError(err.message));
}, []);
if (error) return <div>Error: {error}</div>;
if (!user) return <div>Loading...</div>;
return <h1>{user.name}</h1>;
}The Jest Test
You can mock global.fetch using jest.fn()
to return a resolved promise containing mock data.
import { render, screen, waitFor } from '@testing-library/react';
import UserProfile from './UserProfile';
describe('UserProfile Component', () => {
beforeEach(() => {
// Clear mocks before each test
jest.restoreAllMocks();
});
test('renders user name after successful fetch', async () => {
// Mock global fetch
jest.spyOn(global, 'fetch').mockImplementation(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({ name: 'John Doe' }),
})
);
render(<UserProfile />);
// Check loading state
expect(screen.getByText('Loading...')).toBeInTheDocument();
// Wait for the mock data to render
const nameElement = await screen.findByText('John Doe');
expect(nameElement).toBeInTheDocument();
expect(global.fetch).toHaveBeenCalledTimes(1);
});
test('renders error message on fetch failure', async () => {
// Mock fetch failure
jest.spyOn(global, 'fetch').mockImplementation(() =>
Promise.resolve({
ok: false,
})
);
render(<UserProfile />);
const errorElement = await screen.findByText('Error: Failed to fetch');
expect(errorElement).toBeInTheDocument();
});
});Method 2: Mocking Fetch with Mock Service Worker (MSW)
While Jest spies work well for simple cases, they can become difficult to maintain in complex applications. Mock Service Worker (MSW) is the recommended industry standard because it intercepts requests at the network level, allowing you to write tests that are closer to real browser behavior.
1. Install MSW
Install MSW in your project:
npm install msw --save-dev2. Set Up the Mock Server
Create a file named src/mocks/server.js to define your
API handlers and start the mock server:
import { setupServer } from 'msw/node';
import { rest } from 'msw';
// Define request handlers
export const handlers = [
rest.get('https://api.example.com/user', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({ name: 'Jane Doe' })
);
}),
];
// Setup the mock server
export const server = setupServer(...handlers);3. Configure Jest to Use MSW
In your Jest setup file (usually src/setupTests.js),
configure the server to listen before your tests run, and clean up
afterward:
import { server } from './mocks/server';
// Establish API mocking before all tests.
beforeAll(() => server.listen());
// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers());
// Clean up after the tests are finished.
afterAll(() => server.close());4. Write the React Test
With MSW configured, you do not need to manually mock
fetch in your individual test files. MSW will automatically
intercept the network requests made by your components.
import { render, screen } from '@testing-library/react';
import UserProfile from './UserProfile';
import { server } from './mocks/server';
import { rest } from 'msw';
describe('UserProfile with MSW', () => {
test('renders user name from MSW handler', async () => {
render(<UserProfile />);
const nameElement = await screen.findByText('Jane Doe');
expect(nameElement).toBeInTheDocument();
});
test('renders error message when server returns 500', async () => {
// Override the default handler for this specific test
server.use(
rest.get('https://api.example.com/user', (req, res, ctx) => {
return res(ctx.status(500));
})
);
render(<UserProfile />);
const errorElement = await screen.findByText('Error: Failed to fetch');
expect(errorElement).toBeInTheDocument();
});
});