React Testing Library logo

React Testing Library

The standard utility for testing React components, emphasizing user-centric queries over implementation details.

npm install react-testing-library
19.5K94.0K/weekv8.0.1717 BUnknown75 issues
Last updated: 2019-05-31
Star history chart for testing-library/react-testing-library

TL;DR

A lightweight solution for testing React components that encourages better testing practices by querying the DOM nodes users interact with.

The de facto standard for React unit testing, replacing Enzyme, and is built on top of DOM Testing Library.

Why React Testing Library?

React Testing Library (RTL) fundamentally changed how React components are tested. Before RTL, tools like Enzyme allowed developers to inspect the internal state and props of components. RTL takes a strict stance against this: if a user can't see it, you shouldn't test it.

  • Behavior Driven: Tests are written from the perspective of the user (e.g., "Click the button that says 'Submit'"), making them resilient to code refactors.
  • Accessibility First: The primary way to find elements is via ARIA roles (getByRole) and text (getByText), forcing developers to write accessible HTML to make their tests pass.
  • No Shallow Rendering: RTL renders the full component tree. This ensures that child components integrate correctly with parents, catching integration bugs that "shallow" rendering would miss.
  • Framework Agnostic Core: It is a wrapper around dom-testing-library, meaning the core concepts (Queries, Waits) apply to Vue, Angular, and Svelte versions as well.

Code Snippet

A typical test using user-event to simulate real user interactions.

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

test('allows the user to login successfully', async () => {
  // 1. Setup user session for interactions
  const user = userEvent.setup();
  
  render(<LoginForm />);

  // 2. Find elements by accessible roles/labels
  const usernameInput = screen.getByLabelText(/username/i);
  const submitButton = screen.getByRole('button', { name: /log in/i });

  // 3. Interact
  await user.type(usernameInput, 'johndoe');
  await user.click(submitButton);

  // 4. Assertions
  // Expect a success message to appear in the document
  expect(await screen.findByText(/welcome back/i)).toBeInTheDocument();
});

This test cares only about the rendered output. You could rewrite LoginForm from a Class Component to Hooks, or use Redux vs Context, and this test would not need to change.

Pros and Cons

No library is perfect; understanding the trade-offs is key to selecting the right tool.

Pros

  • Refactoring Confidence: Because tests are decoupled from implementation details, you can refactor code freely without fixing broken tests.
  • Accessibility Enforcement: If your HTML is semantic (proper labels, roles), writing tests is easy. If it's div-soup, writing tests is hard. This aligns incentives correctly.
  • Ecosystem Standard: It comes pre-installed with Create React App, Next.js, and is the recommended solution in the official React docs.

Cons

  • Learning Curve: Developers coming from Enzyme often struggle with the inability to "find by component name" or check props directly.
  • Verbosity: Testing complex interactions usually requires more setup code compared to shallow rendering.
  • Performance: Rendering the full tree (instead of shallow) can be slower for massive components, though this is rarely a bottleneck in modern CI.

Comparison with Other Testing Libraries

The table below outlines the positioning differences between React Testing Library and others:

LibraryDesign PhilosophyBest ForPain Points
React Testing LibraryUser-Centric
Test what the user sees. Ignore the underlying code structure.
Unit/Integration
Testing individual components or page sections in isolation (JSDOM).
Complex Setup
Mocking contexts/providers for deeply nested components can be tedious.
Enzyme (Deprecated)Implementation-Centric
Allowed reading state/props directly. Encouraged shallow rendering.
Legacy Codebases
Older React apps (class components) that haven't migrated yet.
Fragility
Tests break constantly when refactoring code, even if the UI works fine.
PlaywrightReal Browser
Tests the full app in a real Chrome/Safari instance.
End-to-End (E2E)
Testing critical user flows across the entire application stack.
Speed
Much slower than RTL (JSDOM) because it launches a real browser.

Ecosystem & Extensions

React Testing Library is rarely used alone; it relies on a suite of companion tools:

  • @testing-library/jest-dom: Adds custom Jest matchers like toBeInTheDocument(), toBeVisible(), and toHaveClass() to make assertions readable.
  • @testing-library/user-event: A companion library that simulates browser events (clicks, typing, hover) more accurately than the built-in fireEvent.
  • MSW (Mock Service Worker): While not part of the library, MSW is the standard for mocking API requests at the network layer during RTL tests.