How to Mock React Hooks

This article explains how to mock React hooks when writing unit tests for your React applications. You will learn how to mock custom hooks using Jest’s mocking utilities, spy on built-in React hooks like useState and useEffect, and use React Testing Library to test hooks in isolation.

Mocking Custom React Hooks

The most common scenario is mocking a custom hook that fetches data or manages complex state. To mock a custom hook, you can use jest.mock() to mock the module containing the hook, and then define its return value in your test suite.

Suppose you have a custom hook called useUser:

// useUser.js
export const useUser = () => {
  return { user: { name: 'John Doe' }, loading: false };
};

To mock this hook in your test file:

import { render, screen } from '@testing-library/react';
import { MyComponent } from './MyComponent';
import { useUser } from './useUser';

// 1. Mock the entire module path
jest.mock('./useUser');

test('renders mocked user data', () => {
  // 2. Define the mocked return value for this specific test
  useUser.mockReturnValue({
    user: { name: 'Jane Smith' },
    loading: false,
  });

  render(<MyComponent />);
  
  expect(screen.getByText('Jane Smith')).toBeInTheDocument();
});

Mocking Built-In React Hooks

Sometimes you need to mock built-in hooks from the core 'react' library, such as useContext or useMemo. You can achieve this by using jest.spyOn().

Here is how to mock the useContext hook:

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

test('mocks useContext', () => {
  // Spy on the useContext hook of the React object
  const useContextSpy = jest.spyOn(React, 'useContext');
  
  // Provide the mock return value
  useContextSpy.mockReturnValue({ theme: 'dark' });

  render(<MyComponent />);

  expect(screen.getByRole('button')).toHaveClass('dark-theme');

  // Clear mock after the test
  useContextSpy.mockRestore();
});

Mocking State Hooks (useState)

While it is generally recommended to test state transitions through user interactions rather than mocking useState directly, you can mock useState using jest.spyOn if your test architecture requires it.

import React from 'react';
import { render } from '@testing-library/react';
import { MyComponent } from './MyComponent';

test('mocks useState setter function', () => {
  const setMock = jest.fn();
  
  // Mock useState to return a specific state and our mock setter
  jest.spyOn(React, 'useState').mockImplementation((initialState) => [initialState, setMock]);

  render(<MyComponent />);
  
  // Trigger action in component and assert setMock was called
});

Testing Hooks without Mocking: renderHook

If you want to test the behavior of a custom hook itself rather than mocking its output in a component, use the renderHook utility provided by @testing-library/react. This avoids mocking altogether and executes the hook in a virtual component.

import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';

test('should increment counter', () => {
  const { result } = renderHook(() => useCounter());

  act(() => {
    result.current.increment();
  });

  expect(result.current.count).toBe(1);
});