How to Mock useEffect in React

Mocking the useEffect hook in React is a common task when unit testing components that perform side effects, such as API calls or event subscriptions. This article explains how to effectively handle useEffect during testing using Jest and React Testing Library. You will learn why mocking the hook directly is usually discouraged, how to mock the external dependencies called inside the hook instead, and how to spy on useEffect when absolutely necessary.


In most cases, you should not mock the useEffect hook itself. useEffect is an implementation detail of your component. Instead, you should mock the external modules, API clients, or functions that are called inside the useEffect hook.

For example, if your component fetches data inside useEffect:

import React, { useState, useEffect } from 'react';
import { fetchUserData } from './api';

export function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUserData(userId).then(data => setUser(data));
  }, [userId]);

  if (!user) return <div>Loading...</div>;
  return <div>{user.name}</div>;
}

To test this component, mock the fetchUserData function using Jest:

import { render, screen, waitFor } from '@testing-library/react';
import { UserProfile } from './UserProfile';
import { fetchUserData } from './api';

// Mock the API module
jest.mock('./api');

test('renders user data after fetching', async () => {
  // Define mock behavior
  fetchUserData.mockResolvedValueOnce({ name: 'John Doe' });

  render(<UserProfile userId="123" />);

  // Verify loading state
  expect(screen.getByText('Loading...')).toBeInTheDocument();

  // Wait for the useEffect side effect to resolve and update the DOM
  await waitFor(() => {
    expect(screen.getByText('John Doe')).toBeInTheDocument();
  });
});

Mocking useEffect Directly (SpyOn Approach)

If you have a specific edge case where you must prevent useEffect from running entirely, or you need to assert that it was called, you can mock the hook using jest.spyOn.

Here is how to spy on and mock the implementation of useEffect:

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

test('should spy on useEffect', () => {
  const useEffectSpy = jest.spyOn(React, 'useEffect');
  
  // Optionally mock the implementation to do nothing
  useEffectSpy.mockImplementation((f) => f());

  render(<MyComponent />);

  expect(useEffectSpy).toHaveBeenCalled();

  // Restore the original implementation after the test
  useEffectSpy.mockRestore();
});

Note that mocking useEffect to execute synchronously (as done with (f) => f()) can alter the standard React lifecycle and lead to unexpected rendering behavior. Use this approach sparingly.


Summary of Best Practices

  1. Avoid Mocking the Hook: Test the outcome of the side effect (e.g., changes in the DOM) rather than the execution of useEffect itself.
  2. Mock External APIs: Use jest.mock() to mock network requests, timers, or third-party libraries invoked inside the hook.
  3. Use act() or waitFor: Ensure your tests wrap state updates caused by asynchronous side effects using utilities from React Testing Library to prevent “not wrapped in act(…)” warnings.