DevAcademy
LearnReactTesting React Components
AdvancedReact

Testing React Components

Learn how to test React components using React Testing Library, focusing on behavior rather than implementation details.

Reading Time

18 min

Lesson

Lesson 39 of 42

What to Test

React Testing Library encourages testing components the way a user actually experiences them — rendering the component, interacting with it, and asserting on what appears on screen — rather than inspecting internal state or implementation details directly.

A Basic Component Test

import { render, screen } from "@testing-library/react";

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

test("renders a greeting with the given name", () => {
  render(<Greeting name="Alice" />);
  expect(screen.getByText("Hello, Alice!")).toBeInTheDocument();
});

Simulating User Interaction

The userEvent library simulates real user interactions — clicks, typing, tabbing — more realistically than firing raw DOM events directly.

Testing a Click Interaction

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

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}

test("increments the count when clicked", async () => {
  const user = userEvent.setup();
  render(<Counter />);

  const button = screen.getByRole("button");
  expect(button).toHaveTextContent("Count: 0");

  await user.click(button);
  expect(button).toHaveTextContent("Count: 1");
});

Common Queries

QueryFinds
getByRoleAn element by its accessible role, like "button" or "heading" (preferred)
getByTextAn element containing specific text
getByLabelTextA form input by its associated label
getByTestIdAn element by a data-testid attribute (last resort)

Testing Asynchronous Behavior

For components that fetch data or update after a delay, findBy* queries wait for an element to appear, and waitFor() waits for an arbitrary assertion to pass — both essential for testing anything involving useEffect or async state updates.

Testing an Async Component

test("shows the user's name after loading", async () => {
  render(<UserProfile userId={1} />);

  expect(screen.getByText("Loading...")).toBeInTheDocument();

  const heading = await screen.findByRole("heading", { name: "Alice" });
  expect(heading).toBeInTheDocument();
});

Avoid Testing Implementation Details

A test that reaches into a component’s internal state or checks how many times a function was called tends to break whenever you refactor, even if the user-facing behavior didn’t change. Testing what’s rendered and how it responds to interaction is far more resilient.

Best Practice

Prefer getByRole over getByTestId whenever possible — it verifies the component is actually accessible (a real button, a real heading) while also being resilient to markup changes that don’t affect behavior.

Interview Questions

Quick Quiz

1. What does React Testing Library encourage testing?

2. Which query is generally preferred for finding elements in a test?

3. Why use findByRole instead of getByRole for content that loads asynchronously?