DevPath · Learn to code ESPTEN

Routing, testing and patterns

Component testing with React Testing Library

Test like the user

React Testing Library (RTL) is the reference tool for testing components. Its philosophy is explicit:

The more your tests resemble the way your software is used, the more confidence they give you.

That's why RTL pushes you to interact with the component as a person would (looking for texts, labels and roles) and not to inspect internal details such as state or method names.

This lesson is conceptual: the exercise runtime does not include RTL. Here you learn the philosophy and the API; the review is done with quizzes.

The basic flow: render, screen, events

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Counter from "./Counter";

test("increments when the button is clicked", async () => {
  render(<Counter />);

  // Search like the user: by what is seen.
  expect(screen.getByText("Count: 0")).toBeInTheDocument();

  // Interact like the user: click the button.
  await userEvent.click(screen.getByRole("button", { name: "Add" }));

  expect(screen.getByText("Count: 1")).toBeInTheDocument();
});

Behavior, not implementation

The underlying idea: if you rename a state variable or change useState to useReducer, the test should not break as long as the interface behaves the same. You are testing observable behavior, not implementation details. This makes tests survive refactors.

Who runs the tests?

RTL only provides the utilities to render and query; it needs a test runner to run them. The two most common are Vitest (fast, integrated with Vite) and Jest (the classic). Both provide test/it, expect and the matchers like toBe or toEqual.

Put this into practice

DevPath is a hands-on course: you read the theory here; in the app you put it into practice with exercises that really run, offline.

Start free in the app →
← Routing with React Router (conceptual)Error boundaries and advanced patterns →