How to Mock Context API in React

Testing React components that rely on the Context API requires a way to simulate context values without rendering your entire application tree. This article provides a straightforward guide on how to mock the React Context API in unit tests using Jest and React Testing Library. We will cover the two most effective methods: wrapping components with a Provider in your tests, and mocking the context hook directly with Jest.

The most reliable way to mock context is by wrapping the component under test with the actual Context Provider and passing a custom value. This closely mimics real-world behavior and avoids brittle implementation details.

1. The Component and Context

Assume you have a ThemeContext and a ThemeButton component that consumes it:

// ThemeContext.js
import { createContext } from 'react';
export const ThemeContext = createContext('light');

// ThemeButton.js
import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

export function ThemeButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Active Theme: {theme}</button>;
}

2. The Unit Test

In your test file, import the ThemeContext.Provider and wrap the component inside the render function of React Testing Library:

import React from 'react';
import { render, screen } from '@testing-library/react';
import { ThemeContext } from './ThemeContext';
import { ThemeButton } from './ThemeButton';

test('renders the component with custom context value', () => {
  render(
    <ThemeContext.Provider value="dark">
      <ThemeButton />
    </ThemeContext.Provider>
  );

  const button = screen.getByRole('button');
  expect(button).toHaveTextContent('Active Theme: dark');
  expect(button).toHaveClass('dark');
});

Method 2: Mocking the Context Hook with Jest

If you want to isolate your component completely or avoid importing the Provider, you can mock the useContext hook or a custom context hook using jest.spyOn or jest.mock.

1. The Custom Hook Component

Suppose you are using a custom hook to consume context:

// useUser.js
import { useContext } from 'react';
import { UserContext } from './UserContext';

export function useUser() {
  return useContext(UserContext);
}

// UserProfile.js
import React from 'react';
import { useUser } from './useUser';

export function UserProfile() {
  const { user } = useUser();
  return <div>Welcome, {user.name}!</div>;
}

2. Mocking the Hook in Jest

You can mock the return value of the custom hook before rendering the component:

import React from 'react';
import { render, screen } from '@testing-library/react';
import { UserProfile } from './UserProfile';
import * as UserHook from './useUser';

test('renders user profile with mocked custom hook', () => {
  // Spy on the hook and mock its return value
  const useUserSpy = jest.spyOn(UserHook, 'useUser');
  useUserSpy.mockReturnValue({ user: { name: 'John Doe' } });

  render(<UserProfile />);

  expect(screen.getByText('Welcome, John Doe!')).toBeInTheDocument();

  // Clean up the spy after the test
  useUserSpy.mockRestore();
});

Creating a Reusable Custom Render Function

If your application uses multiple contexts (e.g., Auth, Theme, Language), wrapping every test component manually can lead to boilerplate code. You can create a custom render function for your tests.

// test-utils.js
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeContext } from './ThemeContext';
import { UserContext } from './UserContext';

const AllProviders = ({ children }) => {
  return (
    <UserContext.Provider value={{ user: { name: 'Test User' } }}>
      <ThemeContext.Provider value="dark">
        {children}
      </ThemeContext.Provider>
    </UserContext.Provider>
  );
};

const customRender = (ui, options) =>
  render(ui, { wrapper: AllProviders, ...options });

export * from '@testing-library/react';
export { customRender as render };

Now, you can import this custom render function in your test files instead of the default React Testing Library import, automatically applying your mocked contexts to all test components.