How to Mock useState Hook in React

This article provides a straightforward guide on how to mock the useState hook in React using Jest. You will learn the step-by-step process of spying on the hook, overriding its default behavior with custom mock functions, and verifying that state setters are called correctly during unit testing.

While testing best practices generally recommend testing component behavior rather than implementation details, mocking the useState hook is sometimes necessary when you need to spy on state setters directly or isolate specific state behaviors.

To mock the useState hook, you can use Jest’s jest.spyOn method to intercept calls to React.useState and return a mock state value along with a mock setter function.

Step-by-Step Implementation

Suppose you have a simple Counter component that increments a value:

// Counter.js
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default Counter;

To test that clicking the button triggers the state setter, you can mock useState in your test file as follows:

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

describe('Counter Component', () => {
  afterEach(() => {
    jest.restoreAllMocks(); // Restore original implementation after each test
  });

  it('should call the mock state setter when the button is clicked', () => {
    // 1. Create a mock function for the state setter
    const setMockCount = jest.fn();

    // 2. Spy on React.useState and override its return value
    const useStateSpy = jest.spyOn(React, 'useState');
    useStateSpy.mockImplementation((initialState) => [initialState, setMockCount]);

    // 3. Render the component
    render(<Counter />);

    // 4. Simulate a user click event
    const button = screen.getByText('Increment');
    fireEvent.click(button);

    // 5. Assert that the mock setter function was called
    expect(setMockCount).toHaveBeenCalledTimes(1);
    expect(setMockCount).toHaveBeenCalledWith(1); 
  });
});

Key Takeaways

  1. Import React as an Object: In order to spy on useState, you must import React as an entire module (i.e., import React from 'react') in both your component and your test file. This allows jest.spyOn(React, 'useState') to reference the method correctly.
  2. Mock Implementation: The mockImplementation function takes the initialState argument (in this case, 0) and returns an array containing that initial state and your mock setter function setMockCount.
  3. Clean Up: Always run jest.restoreAllMocks() or useStateSpy.mockRestore() after your tests to ensure that the real useState hook is restored for other test suites.