100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Testing

Testing React Components with React Testing Library

Learn how to test React components the way a user experiences them, using render, screen queries, userEvent, and async assertions.

Setup & TeardownIntermediate10 min readJul 10, 2026
Analogies

Testing Components Like a User

React Testing Library (RTL), built on top of Jest, is guided by the principle that tests should resemble the way users actually interact with your app. Rather than inspecting a component's internal state or private methods, RTL renders components into a real DOM and asserts on visible, accessible output.

🏏

Cricket analogy: React Testing Library's philosophy of testing what the user sees is like judging a batsman purely by runs scored and how the ball leaves the bat, not by inspecting his internal muscle-memory or stance mechanics.

Rendering and Querying with screen

render(<Component />) mounts a component into a test DOM attached to document.body, and screen exposes query functions like getByRole, getByText, and getByLabelText to locate elements the same way a real user or assistive technology would find them.

🏏

Cricket analogy: Using screen.getByRole('button', { name: /submit/i }) is like a scorer identifying Rohit Sharma specifically by his batting position and jersey number rather than guessing from a blurry photo.

Simulating Interactions with userEvent

@testing-library/user-event simulates realistic browser interaction sequences — clicks, typing, hovering, and selecting — firing the full chain of events a real user's action would trigger, unlike the lower-level fireEvent which dispatches a single synthetic event.

🏏

Cricket analogy: Using userEvent.click(button) to simulate a fan pressing 'buy tickets' is like simulating a genuine appeal from Ravindra Jadeja rather than an umpire being told directly that a wicket occurred—it goes through the full realistic interaction path.

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

test('shows an error when submitting an empty form', async () => {
  const user = userEvent.setup();
  render(<LoginForm onSubmit={jest.fn()} />);

  await user.click(screen.getByRole('button', { name: /log in/i }));

  expect(
    await screen.findByText(/email is required/i)
  ).toBeInTheDocument();
});

test('calls onSubmit with entered credentials', async () => {
  const handleSubmit = jest.fn();
  const user = userEvent.setup();
  render(<LoginForm onSubmit={handleSubmit} />);

  await user.type(screen.getByLabelText(/email/i), 'asha@example.com');
  await user.type(screen.getByLabelText(/password/i), 'hunter2');
  await user.click(screen.getByRole('button', { name: /log in/i }));

  expect(handleSubmit).toHaveBeenCalledWith({
    email: 'asha@example.com',
    password: 'hunter2',
  });
});

Handling Asynchronous UI

When a component's output changes asynchronously — after an API call resolves or a timeout fires — use findBy* queries, which return a promise that retries until the element appears, or wrap assertions in waitFor() for more general async conditions.

🏏

Cricket analogy: Using await screen.findByText('100*') to wait for a century notification is like a commentator waiting for the scoreboard to actually update to 100 before announcing it, rather than assuming it happened instantly.

React Testing Library exposes a query priority guide: prefer getByRole, then getByLabelText, then getByPlaceholderText or getByText, and reserve getByTestId for cases with no accessible alternative — this mirrors how assistive technology and real users find elements.

Using fireEvent.click() instead of userEvent.click() skips realistic browser event sequences (like focus, mousedown, mouseup) that userEvent simulates, which can mask bugs that only show up with real user interaction sequences.

  • React Testing Library encourages testing components the way a user actually interacts with them.
  • render() mounts a component into a virtual DOM attached to document.body for querying.
  • screen.getByRole/getByText/getByLabelText locate elements the same way assistive technology would.
  • userEvent simulates realistic browser interaction sequences, preferred over fireEvent for user actions.
  • findBy* queries and waitFor() handle asynchronous UI updates like API responses or loading states.
  • Query priority favors accessible queries (getByRole) over getByTestId as a last resort.

Practice what you learned

Was this page helpful?

Topics covered

#Testing#JestStudyNotes#TestingQA#TestingReactComponentsWithReactTestingLibrary#React#Components#Library#Like#StudyNotes#SkillVeris