JS Guide
HomeQuestionsTopicsCompaniesResources
BookmarksSearch

Built for developers preparing for JavaScript, React & TypeScript interviews.

ResourcesQuestionsSupport
HomeQuestionsSearchProgress
HomeTopicstestingReact Testing Library
PrevNext
testing
intermediate
15 min read

React Testing Library

component-testing
getByRole
jest-dom
queries
react-testing-library
screen
userEvent

Testing React components the way users interact with them using React Testing Library's query priority hierarchy, userEvent for realistic interactions, and the philosophy of testing behavior over implementation details.

Key Points

1User-Centric Philosophy

Test what users see and do, not implementation details. This makes tests resilient to refactoring -- if the component looks and behaves the same, tests pass regardless of internal changes.

2Query Priority Hierarchy

Prefer getByRole (accessibility-first), then getByLabelText, getByText, and getByTestId as a last resort. This priority ensures tests mirror how users and assistive technology find elements.

3userEvent over fireEvent

userEvent simulates complete interaction sequences (focus, pointer, mouse, click) like a real browser, while fireEvent only dispatches individual DOM events.

4Query Variants

getBy throws if not found (elements that must exist), queryBy returns null (testing absence), and findBy returns a promise (waiting for async elements to appear).

What You'll Learn

  • Apply the query priority hierarchy to choose the right selector for each element
  • Use userEvent for realistic user interaction simulation in tests
  • Choose between getBy, queryBy, and findBy variants based on the testing scenario
  • Avoid common anti-patterns like testing implementation details or using container.querySelector

Deep Dive

React Testing Library (RTL) is the standard library for testing React components. Its core philosophy -- 'The more your tests resemble the way your software is used, the more confidence they can give you' -- drives a fundamentally different approach from enzyme-style testing that inspects component internals.

Core Philosophy

RTL encourages testing components from the user's perspective. Instead of checking internal state, prop values, or instance methods, you verify what the user sees and how the component responds to interactions. This makes tests resilient to refactoring -- you can completely rewrite a component's internals, and if it still looks and behaves the same, tests pass.

Query Priority (Most Important Concept)

RTL provides multiple ways to find elements, ordered by preference:

  1. getByRole: The top priority. Queries by ARIA role, which mirrors how screen readers and assistive technology find elements. Most HTML elements have implicit roles (button, link, heading, textbox).
JavaScript
screen.getByRole('button', { name: 'Submit' });
screen.getByRole('heading', { level: 2 });
  1. getByLabelText: For form elements associated with a label. This is how users find form fields.
JavaScript
screen.getByLabelText('Email address');
  1. getByPlaceholderText: When no label exists (not ideal, but realistic).

  2. getByText: For non-interactive elements where the text content identifies the element.

JavaScript
screen.getByText('Welcome back!');
  1. getByDisplayValue: For inputs with current values.

  2. getByAltText: For images.

  3. getByTitle: For elements with title attributes.

  4. getByTestId: Last resort. Use data-testid attributes when no semantic query works. This is an escape hatch, not the default.

Query Variants

Each query comes in three variants:

  • getBy: Throws if element not found (synchronous, for elements that should exist)
  • queryBy: Returns null if not found (use for asserting absence: expect(queryByRole('alert')).not.toBeInTheDocument())
  • findBy: Returns a promise, waits for element to appear (use for async rendering)

userEvent vs fireEvent

userEvent from @testing-library/user-event simulates full user interaction sequences, not just individual DOM events:

JavaScript
import userEvent from '@testing-library/user-event';
 
const user = userEvent.setup();
await user.click(screen.getByRole('button', { name: 'Submit' }));
await user.type(screen.getByRole('textbox'), 'hello');

userEvent.click() fires focus, pointerdown, mousedown, pointerup, mouseup, and click events in the correct order, just like a real browser click. fireEvent.click() only dispatches the click event. Always prefer userEvent for realistic interaction testing.

Common Patterns

Waiting for async updates:

JavaScript
await waitFor(() => {
  expect(screen.getByText('Loaded')).toBeInTheDocument();
});

Testing absence:

JavaScript
expect(screen.queryByRole('alert')).not.toBeInTheDocument();

Testing within a container:

JavaScript
const nav = screen.getByRole('navigation');
within(nav).getByRole('link', { name: 'Home' });

jest-dom Custom Matchers

The @testing-library/jest-dom package adds DOM-specific matchers:

  • toBeInTheDocument(): Element exists in the DOM
  • toBeVisible(): Element is visible to the user
  • toBeDisabled() / toBeEnabled(): Form element state
  • toHaveTextContent(text): Element contains specific text
  • toHaveAttribute(attr, value): Element has an HTML attribute
  • toHaveClass(className): Element has a CSS class

Anti-Patterns to Avoid

  • Testing internal component state directly
  • Using container.querySelector instead of semantic queries
  • Testing that specific functions were called (test what the user sees instead)
  • Wrapping every interaction in act() manually (RTL handles this automatically)

Fun Fact

React Testing Library was created by Kent C. Dodds in 2018 as a response to Enzyme, which encouraged testing implementation details. RTL's philosophy proved so popular that it spawned an entire family: Testing Library for Vue, Angular, Svelte, and even plain DOM, all sharing the same query API.

Learn These First

Matchers and Assertions

beginner

Unit Testing Fundamentals

beginner

Continue Learning

Testing Custom React Hooks

intermediate

Integration Tests vs Unit Tests

intermediate

Mocking Functions, Modules, and APIs

intermediate

Practice What You Learned

How do you test React components with React Testing Library?
mid
react-testing-library
React Testing Library tests components from the user's perspective. Use render() to mount components, query methods (getByRole, getByText, etc.) to find elements, fireEvent or userEvent for interactions, and waitFor for async updates.
Previous
Performance Testing and Budgets
Next
Test Lifecycle Hooks
PrevNext