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-dev

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