Clean • Professional
React Testing Library (RTL) is a popular testing library for React that focuses on testing components the way users interact with them. Unlike traditional unit tests, RTL encourages testing user behavior instead of implementation details, making your tests more reliable and maintainable.
React Testing Library is a lightweight library that works with Jest to:
If you are using Create React App, RTL is already included. Otherwise, install it with:
npm install --save-dev @testing-library/react @testing-library/jest-dom
@testing-library/react → renders and queries React components@testing-library/jest-dom → provides custom matchers like .toBeInTheDocument()Suppose you have a simple Greeting component:
// Greeting.jsx
import React from "react";
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
export default Greeting;
Test with RTL:
// Greeting.test.jsx
import React from "react";
import { render, screen } from "@testing-library/react";
import Greeting from "./Greeting";
test("renders the greeting message", () => {
render(<Greeting name="Alice" />);
const greeting = screen.getByText("Hello, Alice!");
expect(greeting).toBeInTheDocument();
});
render() → mounts the component in a virtual DOMscreen.getByText() → finds the element containing the textexpect(...).toBeInTheDocument() → checks if the element is presentRTL provides multiple ways to query elements:
| Method | Description | Example |
|---|---|---|
getByText | Finds elements by text content | screen.getByText("Hello") |
getByRole | Finds elements by ARIA role | screen.getByRole("button") |
getByLabelText | Finds form elements by label | screen.getByLabelText("Username") |
queryBy... | Returns null if element not found (useful for negative tests) | screen.queryByText("Goodbye") |
findBy... | Returns a promise for async elements | await screen.findByText("Loaded!") |
RTL works well with fireEvent or userEvent to simulate user actions:
// Counter.jsx
import React, { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default Counter;
Test button click:
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import Counter from "./Counter";
test("increments counter when button is clicked", () => {
render(<Counter />);
const button = screen.getByText("Increment");
fireEvent.click(button);
expect(screen.getByText("Count: 1")).toBeInTheDocument();
});
fireEvent.click(button) simulates a clickFor components that fetch data or have async behavior:
import React, { useEffect, useState } from "react";
function FetchMessage() {
const [message, setMessage] = useState("");
useEffect(() => {
setTimeout(() => setMessage("Hello from API"), 500);
}, []);
return <div>{message}</div>;
}
export default FetchMessage;
Async test using findByText:
import { render, screen } from "@testing-library/react";
import FetchMessage from "./FetchMessage";
test("renders async message", async () => {
render(<FetchMessage />);
const message = await screen.findByText("Hello from API");
expect(message).toBeInTheDocument();
});
findByText waits for the element to appear