![]()
React Testing Library (RTL) is a testing utility that encourages testing your components the way users would interact with them. It provides simple utilities for rendering components, querying elements, and simulating events, with a strong emphasis on accessibility and user-centric testing.
This guide will walk through how to write tests for your React components using React Testing Library, covering common patterns and practices.
1. Installing React Testing Library
If you are using Create React App (CRA), React Testing Library comes pre-installed. Otherwise, you can manually install it by running the following command:
npm install --save-dev @testing-library/react @testing-library/jest-dom
@testing-library/react: Core package for rendering components and interacting with the DOM.@testing-library/jest-dom: Extends Jest’sexpectwith useful matchers liketoBeInTheDocument,toHaveTextContent, etc.
2. Basic Testing Workflow
The basic workflow for testing a React component involves:
- Render the Component: Use
render()to render the component into a test DOM. - Query the Elements: Use queries to locate DOM elements based on various criteria (e.g.,
getByText,getByRole). - Interact with Elements: Simulate user interactions like clicks, typing, etc., using
fireEvent. - Assert the Results: Use Jest assertions to check the expected behavior.
3. Example Component to Test
Let’s create a simple component that we can use to demonstrate testing.
Example: Counter.js
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
return (
<div>
<h1>Counter: {count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
export default Counter;
This is a simple counter component with two buttons to increment and decrement the count.
4. Basic Test for the Counter Component
Now, let’s write tests for the Counter component. We’ll check if:
- The initial count is displayed correctly.
- Clicking the “Increment” button increases the count.
- Clicking the “Decrement” button decreases the count.
Test: Counter.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
describe('Counter Component', () => {
test('displays initial count', () => {
render(<Counter />);
// Get the element displaying the count
const countElement = screen.getByText(/Counter:/i);
// Assert that the initial count is 0
expect(countElement).toHaveTextContent('Counter: 0');
});
test('increments the count when Increment button is clicked', () => {
render(<Counter />);
const incrementButton = screen.getByText(/Increment/i);
const countElement = screen.getByText(/Counter:/i);
// Click the increment button
fireEvent.click(incrementButton);
// Assert that the count increased
expect(countElement).toHaveTextContent('Counter: 1');
});
test('decrements the count when Decrement button is clicked', () => {
render(<Counter />);
const decrementButton = screen.getByText(/Decrement/i);
const countElement = screen.getByText(/Counter:/i);
// Click the decrement button
fireEvent.click(decrementButton);
// Assert that the count decreased
expect(countElement).toHaveTextContent('Counter: -1');
});
});
5. Explanation of the Test Code
render(<Counter />): This renders theCountercomponent into the test DOM.screen.getByText(/Counter:/i): This query finds the element displaying the textCounter:, using a case-insensitive match (iflag).fireEvent.click(incrementButton): This simulates a click event on the “Increment” button.expect(countElement).toHaveTextContent('Counter: 1'): This assertion checks that the text content of the count element has changed toCounter: 1.
6. Different Querying Methods
React Testing Library provides several methods for querying elements. Here are some of the most common:
getByText: Finds elements by their text content.getByRole: Finds elements by their role, like buttons, headings, etc.getByLabelText: Finds form elements like input fields by their associated label text.getByTestId: Finds elements by adata-testidattribute (less preferred; use it sparingly).getByPlaceholderText: Finds input elements by their placeholder text.
Example of getByRole:
test('displays two buttons', () => {
render(<Counter />);
// Query buttons by role
const buttons = screen.getAllByRole('button');
// Assert there are two buttons
expect(buttons).toHaveLength(2);
});
7. Simulating User Interactions
React Testing Library allows you to simulate user interactions like clicking, typing, or submitting forms. The most common method is using fireEvent.
Simulating a Click:
fireEvent.click(button);
Simulating Typing:
fireEvent.change(inputElement, { target: { value: 'New Value' } });
8. Async Testing (Handling Delays or API Calls)
In real-world apps, you may need to handle asynchronous behavior, such as waiting for data from an API or waiting for an element to appear after a delay. React Testing Library provides several utilities for this, like findBy queries and waitFor.
Example: Testing Async Behavior
Suppose we have a component that fetches data asynchronously.
Example: DataFetcher.js
import React, { useState, useEffect } from 'react';
const DataFetcher = () => {
const [data, setData] = useState(null);
useEffect(() => {
setTimeout(() => {
setData('Fetched Data');
}, 2000);
}, []);
return <div>{data ? data : 'Loading...'}</div>;
};
export default DataFetcher;
Test: DataFetcher.test.js
import { render, screen } from '@testing-library/react';
import DataFetcher from './DataFetcher';
test('displays loading text initially, then fetched data', async () => {
render(<DataFetcher />);
// Initially, the loading text should be displayed
expect(screen.getByText(/Loading.../i)).toBeInTheDocument();
// Wait for the data to be fetched
const dataElement = await screen.findByText(/Fetched Data/i);
// After 2 seconds, the fetched data should be displayed
expect(dataElement).toBeInTheDocument();
});
Explanation:
findByText: This is an asynchronous query that waits for the element with the specified text to appear.await: We useawaitto wait for the element to appear before making assertions.
9. Cleaning Up Between Tests
React Testing Library automatically cleans up the rendered components between tests. This ensures that one test doesn’t affect the next.
