How to Test useContext Hook in React

Testing the useContext hook in React is essential for ensuring that your state management and global data flows work correctly across your application. This article provides a straightforward guide on how to test components that consume context using React Testing Library and Jest. You will learn the two most effective approaches: wrapping your components directly in a mock provider within individual tests, and creating a reusable custom render function for scaled testing.

The Challenge of Testing useContext

When a React component uses the useContext hook, it relies on a parent Provider to supply its value. If you attempt to render the component in a test environment without this provider, the hook will return the default value defined when the context was created, or undefined.

To test these components accurately, you must provide the context value during the test execution.


The simplest and most common way to test a component using useContext is to wrap the component under test inside the context’s Provider directly within your test file. This allows you to pass mock values to the provider and assert how the component reacts.

Code Example

Imagine you have a theme context and a component that displays the current theme:

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

export const ThemeContext = createContext('light');

export const ThemeComponent = () => {
  const theme = useContext(ThemeContext);
  return <div>Current theme is {theme}</div>;
};

To test ThemeComponent, wrap it in ThemeContext.Provider in your test suite:

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

test('renders the correct context value', () => {
  render(
    <ThemeContext.Provider value="dark">
      <ThemeComponent />
    </ThemeContext.Provider>
  );

  // Assert that the component consumed the "dark" value
  expect(screen.getByText('Current theme is dark')).toBeInTheDocument();
});

test('falls back to default value when no provider is present', () => {
  render(<ThemeComponent />);

  // Assert that the component uses the default "light" value
  expect(screen.getByText('Current theme is light')).toBeInTheDocument();
});

Method 2: Creating a Custom Render Function

If your application uses multiple contexts (e.g., Theme, Auth, User Settings) across many components, wrapping every component manually in every test becomes repetitive. Creating a reusable custom render function simplifies this process.

Step 1: Create the Custom Render Helper

You can create a custom render function that automatically wraps components in the required providers:

// test-utils.js
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeContext } from './ThemeContext';

const AllTheProviders = ({ children }) => {
  return (
    <ThemeContext.Provider value="dark">
      {children}
    </ThemeContext.Provider>
  );
};

const customRender = (ui, options) =>
  render(ui, { wrapper: AllTheProviders, ...options });

// Re-export everything from React Testing Library
export * from '@testing-library/react';

// Override the render method
export { customRender as render };

Step 2: Use the Custom Render in Tests

Now, you can import your custom render function in your test files instead of the standard library render. The context provider will be applied automatically:

// ThemeComponent.test.js
import React from 'react';
import { render, screen } from './test-utils'; // Import from your custom helper
import { ThemeComponent } from './ThemeComponent';

test('renders with the custom render provider value', () => {
  render(<ThemeComponent />);

  // Assert that the component uses the default provider value ("dark")
  expect(screen.getByText('Current theme is dark')).toBeInTheDocument();
});

Testing Contexts that Update State

If your context provider includes functions to update the state (such as a toggle function), you should test that the component can trigger these updates.

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

const ThemeContext = createContext();

const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');
  const toggleTheme = () => setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
  
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

const ThemeToggler = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);
  return (
    <button onClick={toggleTheme}>
      Active: {theme}
    </button>
  );
};

test('updates context state when action is triggered', () => {
  render(
    <ThemeProvider>
      <ThemeToggler />
    </ThemeProvider>
  );

  const button = screen.getByRole('button');
  expect(button).toHaveTextContent('Active: light');

  // Trigger the context function
  fireEvent.click(button);

  // Assert that the state updated in the provider and re-rendered the component
  expect(button).toHaveTextContent('Active: dark');
});