~/
Testing React
Quiz
...

Testing React

intermediate · updated Tue Sep 08 2026Contribute

Learn how to write comprehensive tests for React components using React Testing Library to ensure your UI is reliable and bug-free.

Testing React

Test what users see and do, not how components work internally. React Testing Library (RTL) encourages this approach.

What to Test

  • Renders correctly with different data
  • Responds to user interactions (clicks, input)
  • Shows the right content based on props or state
  • Handles loading, error, and empty states

Don’t test implementation details like internal state or hook calls.

Setup

Install the required packages.

npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom

Configure Vitest with a DOM environment.

// vitest.config.js
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    environment: "jsdom",
    setupFiles: ["./setup.js"],
  },
});
// setup.js
import "@testing-library/jest-dom";

Basic Test

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

test("increments count on click", async () => {
  render(<Counter />);
  const button = screen.getByRole("button", { name: /count/i });
  await userEvent.click(button);
  expect(button).toHaveTextContent("1");
});

Querying Elements

Use queries that reflect what users see.

screen.getByRole("button", { name: "Submit" });  // Accessible role
screen.getByText("Welcome");                      // Text content
screen.getByLabelText("Email");                   // Form labels
screen.getByPlaceholderText("Search");            // Placeholder
screen.queryByText("Not here");                   // Returns null if missing

Prefer getByRole and getByLabelText over getByTestId.

Testing Async Behavior

test("loads user data", async () => {
  render(<UserProfile userId={1} />);
  expect(await screen.findByText("John Doe")).toBeInTheDocument();
});

Use findBy for elements that appear after an async operation.

Mocking

Mock API calls with vi.fn().

vi.mock("./api", () => ({
  fetchUser: vi.fn(),
}));

import { fetchUser } from "./api";

test("displays error on failure", async () => {
  fetchUser.mockRejectedValue(new Error("Network error"));
  render(<UserProfile userId={1} />);
  expect(await screen.findByText("Error")).toBeInTheDocument();
});

Best Practices

  • Test user behavior, not implementation
  • Use accessible queries (getByRole, getByLabelText)
  • Use userEvent over fireEvent for realistic interactions
  • Mock at the network level, not component internals
  • Keep tests focused — one behavior per test

Common Mistakes

  • Testing internal state or hook return values
  • Using getByTestId as the first choice
  • Writing tests that depend on rendering order
  • Not cleaning up between tests (RTL handles this automatically)
  • Over-mocking — mock only what you need