React Testing Library Basics
Learn RTL philosophy, setup, rendering, and writing your first component test.
Testing Philosophy
React Testing Library (RTL) guides you to test components the way users interact with them—by visible text, labels, and roles—not implementation details like state variable names or component class instances.
Tests that mirror user behavior survive refactors. When a test breaks because you renamed an internal hook but UI unchanged, that test was coupled to implementation.
- Query what users see and do, not internal component API
- Avoid testing library internals or child component props unless integration requires
- Confidence comes from behavior assertions, not snapshot-only coverage
Installation and Setup
Install @testing-library/react, @testing-library/jest-dom, and user-event. Configure jest-dom matchers in setupFilesAfterEnv. Use @testing-library/react with Vitest by matching test environment to jsdom.
Import render and screen from @testing-library/react in every component test file.
npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event // test/setup.ts import "@testing-library/jest-dom";
First Component Test
render mounts component into jsdom document. screen queries document globally. Assert with expect and jest-dom matchers like toBeInTheDocument and toHaveTextContent.
Start with simple presentational component before forms and async data fetching.
import { render, screen } from "@testing-library/react";
import { Greeting } from "./Greeting";
test("greets by name", () => {
render(<Greeting name="Ada" />);
expect(screen.getByText("Hello, Ada")).toBeInTheDocument();
});Rendering Components
render returns utilities: rerender for prop updates, unmount for cleanup, container rarely needed. Wrap with providers via wrapper option or custom render helper.
Strict Mode double-invoking effects in React 18 dev may affect test counts—use waitFor for async assertions.
- Default container is document.body appended div
- Use baseElement option if portaling outside default
- cleanup automatic after each test in RTL v13+ with Jest
function renderWithTheme(ui: React.ReactElement) {
return render(ui, { wrapper: ThemeProvider });
}Relationship to Jest and Vitest
RTL is renderer and query library; Jest or Vitest is test runner. Same RTL API across runners—only config differs.
Pair with MSW for network mocking and jest-dom for DOM assertions in both ecosystems.
- Import test from vitest when migrating runner, keep RTL imports same
- Avoid Enzyme shallow render—deprecated pattern incompatible with modern React
- Official docs: testing-library.com/docs/react-testing-library/intro