How to Mock React State in Unit Tests
This article explains how to mock React state when writing unit tests
for your components. We will cover the industry-standard approaches
using Jest and React Testing Library, including mocking the
useState hook directly, mocking custom hooks, and the
recommended practice of triggering state changes through simulated user
interactions.
Method 1: Triggering State Changes via User Events (Recommended)
The most resilient way to test React state is by not mocking the
state itself, but rather testing the behavior that triggers the state
change. Testing implementation details like useState makes
your tests fragile when refactoring.
Using React Testing Library, you can simulate user interactions to assert that the UI updates correctly when state changes:
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Counter from './Counter';
test('increments the counter state on click', async () => {
render(<Counter />);
const button = screen.getByRole('button', { name: /increment/i });
const countDisplay = screen.getByTestId('count-value');
expect(countDisplay).toHaveTextContent('0');
await userEvent.click(button);
expect(countDisplay).toHaveTextContent('1');
});Method 2: Mocking useState Directly with Jest
If you must mock the state value directly to test how a component
renders under specific state conditions, you can spy on
React.useState. This is useful for isolating complex state
logic.
Here is how to mock the return value of useState:
import React from 'react';
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
test('mocks the initial state value', () => {
const mockState = 'Mocked Value';
const setMockState = jest.fn();
// Spy on React.useState and override its return value
jest.spyOn(React, 'useState').mockImplementation(() => [mockState, setMockState]);
render(<MyComponent />);
expect(screen.getByText('Mocked Value')).toBeInTheDocument();
});Note: Always call jest.restoreAllMocks() in your
afterEach block to prevent the mocked state from leaking
into other tests.
Method 3: Mocking Custom Hooks
If your state is managed inside a custom hook, mocking the hook is
much cleaner and less intrusive than mocking React’s built-in
useState.
For example, if you have a custom hook called
useUserStatus:
import { render, screen } from '@testing-library/react';
import Profile from './Profile';
import { useUserStatus } from './useUserStatus';
// Mock the entire module containing the custom hook
jest.mock('./useUserStatus');
test('displays online status when user is online', () => {
// Define the mocked return value for this specific test
useUserStatus.mockReturnValue({ isOnline: true, status: 'Active' });
render(<Profile />);
expect(screen.getByText('Online')).toBeInTheDocument();
});