How to Mock React Lists in React Tests
Testing components that render lists often requires isolating your code to ensure tests remain reliable, fast, and focused. This article provides a straightforward guide on how to mock React lists in your unit and integration tests using Jest and React Testing Library (RTL). You will learn how to mock array data, mock child list components, and verify list rendering behavior without overcomplicating your test suite.
Scenario 1: Mocking List Data (Props)
The simplest way to test a component that renders a list is by passing mock data directly into its props. This ensures you control the input and can assert the output accurately.
Suppose you have a UserList component:
// UserList.js
import React from 'react';
export default function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}To mock the list in your test, define a mock array and pass it to the component:
// UserList.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import UserList from './UserList';
const mockUsers = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
test('renders a list of users', () => {
render(<UserList users={mockUsers} />);
const listItems = screen.getAllByRole('listitem');
expect(listItems).toHaveLength(mockUsers.length);
expect(listItems[0]).toHaveTextContent('Alice');
expect(listItems[1]).toHaveTextContent('Bob');
});Scenario 2: Mocking a Child List Component
When testing a parent component (like a Dashboard), you may want to mock a child list component to prevent rendering its complex internal logic, API requests, or heavy styling.
Suppose you have a Dashboard component that imports
UserList:
// Dashboard.js
import React from 'react';
import UserList from './UserList';
export default function Dashboard() {
const users = [{ id: 1, name: 'Alice' }];
return (
<div>
<h1>Admin Dashboard</h1>
<UserList users={users} />
</div>
);
}You can mock the UserList child component using
jest.mock(). This replaces the actual component with a
simplified dummy component during the test run:
// Dashboard.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import Dashboard from './Dashboard';
// Mock the child component
jest.mock('./UserList', () => {
return function DummyUserList({ users }) {
return (
<div data-testid="mock-user-list">
Mocked List Items Count: {users.length}
</div>
);
};
});
test('renders dashboard with mocked user list', () => {
render(<Dashboard />);
// Verify the parent header renders
expect(screen.getByRole('heading', { name: /admin dashboard/i })).toBeInTheDocument();
// Verify the mocked child list component renders with expected props
const mockList = screen.getByTestId('mock-user-list');
expect(mockList).toBeInTheDocument();
expect(mockList).toHaveTextContent('Mocked List Items Count: 1');
});Scenario 3: Mocking API Responses for Lists
If your component fetches list data internally (e.g., using
useEffect or React Query), you should mock the network
request or the API client module rather than the component itself.
Suppose your component fetches a list of items:
// ProductList.js
import React, { useState, useEffect } from 'react';
export default function ProductList() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products')
.then(res => res.json())
.then(data => setProducts(data));
}, []);
return (
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}To test this, mock the global fetch function to resolve
with your mocked list data:
// ProductList.test.js
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import ProductList from './ProductList';
const mockProducts = [
{ id: 101, name: 'Laptop' },
{ id: 102, name: 'Phone' },
];
beforeEach(() => {
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve(mockProducts),
})
);
});
afterEach(() => {
jest.restoreAllMocks();
});
test('fetches and renders products list', async () => {
render(<ProductList />);
// Wait for the async API call to complete and render items
await waitFor(() => {
expect(screen.getAllByRole('listitem')).toHaveLength(2);
});
expect(screen.getByText('Laptop')).toBeInTheDocument();
expect(screen.getByText('Phone')).toBeInTheDocument();
});