How to Test Prop Drilling in React

Prop drilling is a common React pattern where data is passed from a higher-level component down through multiple layers of intermediate components to a deeply nested child. Testing this pattern ensures that data flows correctly through your component tree without breaking. This article explores the best strategies for testing prop drilling using React Testing Library (RTL) and Jest, comparing integration testing with component mocking.

The most reliable way to test prop drilling is through integration testing. Instead of testing each intermediate component in isolation, you render the top-level parent component and assert that the deeply nested child component correctly receives and displays the data. This approach mimics real user behavior and ensures your refactors won’t break the application.

Example Scenario

Imagine a Grandparent component that receives a username prop and passes it through a Parent component down to a Child component.

// Components.js
export const Child = ({ username }) => {
  return <div>Welcome, {username}!</div>;
};

export const Parent = ({ username }) => {
  return <Child username={username} />;
};

export const Grandparent = ({ username }) => {
  return <Parent username={username} />;
};

Writing the Test

Using React Testing Library, you render the Grandparent component and verify that the Child component displays the expected output.

// Grandparent.test.js
import { render, screen } from '@testing-library/react';
import { Grandparent } from './Components';

test('passes username prop down to the Child component', () => {
  render(<Grandparent username="JaneDoe" />);
  
  // Assert that the child component successfully rendered the drilled prop
  const welcomeMessage = screen.getByText('Welcome, JaneDoe!');
  expect(welcomeMessage).toBeInTheDocument();
});

Approach 2: Mocking Child Components

If the nested child components are heavy, perform API calls, or contain complex logic that you do not want to execute during a parent unit test, you can mock the intermediate or child components. This allows you to verify that the prop was passed without rendering the actual child UI.

Writing the Mock Test

In this approach, you mock the Parent component to intercept the props it receives from the Grandparent component.

// Grandparent.mock.test.js
import { render } from '@testing-library/react';
import { Grandparent } from './Components';

// Mock the Parent component to spy on its props
jest.mock('./Components', () => {
  const originalModule = jest.requireActual('./Components');
  return {
    ...originalModule,
    Parent: jest.fn(({ username }) => <div data-testid="mock-parent">{username}</div>),
  };
});

import { Parent } from './Components';

test('Grandparent passes the correct prop to Parent', () => {
  render(<Grandparent username="Alex" />);
  
  // Verify that the mocked Parent component was called with the correct prop
  expect(Parent).toHaveBeenCalledWith(
    expect.objectContaining({ username: 'Alex' }),
    expect.anything()
  );
});

Best Practices for Testing Prop Drilling