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();
});
rendermounts the component in a test DOM.screenprovides the queries. The preferred ones look for what the user perceives:getByRole(the recommended one): searches by the accessibility role (button,heading,textbox...), optionally with its accessible name.getByText,getByLabelText: by the visible text or the label of a field.
fireEventdispatches low-level DOM events.userEvent(from@testing-library/user-event) is a higher layer that simulates more realistic interactions (typing character by character, focus, hover) and is usually the preferred option.
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.