R

React Handbook

Clean • Professional

React Testing Library – Testing React Components Effectively

2 minute

React Testing Library

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.

What is React Testing Library?

React Testing Library is a lightweight library that works with Jest to:

  • Render React components in a virtual DOM for testing
  • Query elements using accessible selectors like text, role, and labels
  • Simulate user interactions such as clicks, typing, and form submissions
  • Assert the expected behavior of components

Installing React Testing Library

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()

Rendering Components with RTL

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 DOM
  • screen.getByText() → finds the element containing the text
  • expect(...).toBeInTheDocument() → checks if the element is present

Querying Elements in RTL

RTL provides multiple ways to query elements:

MethodDescriptionExample
getByTextFinds elements by text contentscreen.getByText("Hello")
getByRoleFinds elements by ARIA rolescreen.getByRole("button")
getByLabelTextFinds form elements by labelscreen.getByLabelText("Username")
queryBy...Returns null if element not found (useful for negative tests)screen.queryByText("Goodbye")
findBy...Returns a promise for async elementsawait screen.findByText("Loaded!")

Simulating User Interactions

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 click
  • The test checks that the UI updates correctly

Async Testing in React

For 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
  • Perfect for testing API calls, timeouts, and loading states

Article 0 of 0