How to Mock useContext Hook in React

Testing React components that rely on the useContext hook is a common requirement in modern frontend development. This article provides a straightforward guide on how to mock the useContext hook in React using Jest and React Testing Library. We will explore the two primary methods for accomplishing this: wrapping the component with a real Context Provider, and directly mocking the React.useContext function.

The most reliable and idiomatic way to mock context is by wrapping your component with the context’s actual Provider during the test. This closely mimics how the component runs in production and avoids modifying React’s internal APIs.

Consider the following component that consumes a UserContext:

// UserProfile.js
import React, { createContext, useContext } from 'react';

export const UserContext = createContext(null);

export const UserProfile = () => {
  const user = useContext(UserContext);
  return (
    <div>
      {user ? <h1>Welcome, {user.name}!</h1> : <h1>Please log in.</h1>}
    </div>
  );
};

To test this component, wrap it with UserContext.Provider in your test file and pass a mock value to the value prop:

// UserProfile.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import { UserContext, UserProfile } from './UserProfile';

test('renders welcome message for logged in user', () => {
  const mockUser = { name: 'John Doe' };

  render(
    <UserContext.Provider value={mockUser}>
      <UserProfile />
    </UserContext.Provider>
  );

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

test('renders login prompt when no user is provided', () => {
  render(
    <UserContext.Provider value={null}>
      <UserProfile />
    </UserContext.Provider>
  );

  expect(screen.getByText('Please log in.')).toBeInTheDocument();
});

Method 2: Mocking React.useContext Directly

If you want to isolate your component completely and avoid importing the context provider into your test files, you can mock the useContext hook directly using jest.spyOn.

This approach is useful when dealing with deeply nested contexts or when the context provider is difficult to instantiate in a test environment.

// UserProfile.spy.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import { UserProfile } from './UserProfile';

describe('UserProfile - Spy Mocking', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });

  test('renders welcome message using spyOn', () => {
    const mockUser = { name: 'Jane Doe' };
    
    // Spy on React.useContext and return the mock user
    jest.spyOn(React, 'useContext').mockReturnValue(mockUser);

    render(<UserProfile />);

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

Note: When using jest.spyOn on React hooks, ensure you restore the mocks after each test to prevent side effects from leaking into other test suites.