Testing ensures your React components work correctly and helps prevent bugs. This section covers testing React components using React Testing Library and Jest.
React Testing Library encourages testing components from a user’s perspective, focusing on behavior rather than implementation details.
Test user interactions using @testing-library/user-event:
import { render, screen } from '@testing-library/react';import userEvent from '@testing-library/user-event';import Greeting from './Greeting';test('renders "Changed!" if the button was clicked', async () => { // Arrange render(<Greeting />); // Act const buttonElement = screen.getByRole('button'); await userEvent.click(buttonElement); // Assert const outputElement = screen.getByText('Changed!'); expect(outputElement).toBeInTheDocument();});test('does not render "good to see you" if the button was clicked', async () => { render(<Greeting />); const buttonElement = screen.getByRole('button'); await userEvent.click(buttonElement); // Use queryByText when element shouldn't exist const outputElement = screen.queryByText('good to see you', { exact: false }); expect(outputElement).toBeNull();});
getByText
Throws an error if element is not found. Use when element must exist.
queryByText
Returns null if element is not found. Use when testing element absence.
Focus on what users see and do, not internal component details.
// Good: Test user-visible behaviorconst button = screen.getByRole('button', { name: /add todo/i });// Bad: Test implementation detailsexpect(wrapper.state().todos).toHaveLength(1);
Use semantic queries
Prefer queries that reflect how users interact with your app.
// Best: By role (most accessible)screen.getByRole('button', { name: /submit/i });// Good: By label textscreen.getByLabelText(/username/i);// Okay: By textscreen.getByText(/hello world/i);// Last resort: By test IDscreen.getByTestId('submit-button');
Keep tests isolated
Each test should be independent and not rely on other tests.
describe('TodoList', () => { test('adds a new todo', () => { // Setup and test in isolation }); test('removes a todo', () => { // Don't depend on previous test });});