React Foundations·Components, state and data

Testing React components

Test behaviour the way a user sees it, not implementation details.

Intermediate 14 min read Lesson 5 of 300 Challenge included
What you will be able to do
  • Render a component and query by role, label or text.
  • Simulate typing and clicking with userEvent.
  • Wait for async content with findBy instead of fixed sleeps.
  • Assert on visible, accessible output, not internal state.

In plain English

React Testing Library renders a component into a simulated DOM so you can interact with it the way a user would. Rendering means mounting the component with render, after which you query for elements by role or accessible text. User events such as clicking and typing are simulated with userEvent and are asynchronous. findBy queries wait for content that appears after data loads, and accessible queries are preferred over test IDs.

Step by step

  1. 1Render the component with render and pass any required props.
  2. 2Find the element with an accessible query such as getByRole or getByLabelText.
  3. 3Act with userEvent to simulate real interaction.
  4. 4Assert on the user-visible result, waiting with findBy when it is async.
SearchForm.test.tsxTSX
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { vi } from 'vitest'
import SearchForm from './SearchForm'

test('submits the typed query', async () => {
  const user = userEvent.setup()
  const onSearch = vi.fn()

  render(<SearchForm onSearch={onSearch} />)

  await user.type(screen.getByLabelText('Search lessons'), 'hooks')
  await user.click(screen.getByRole('button', { name: 'Search' }))

  expect(onSearch).toHaveBeenCalledWith('hooks')
})

Common mistakes

  • Querying by a CSS class couples the test to styling, which changes without changing behaviour.
  • Using waitFor with a fixed delay is slow and flaky compared to a findBy query.
  • Forgetting that userEvent is asynchronous means the test finishes before the interaction lands.
  • Asserting on internal state instead of visible output gives tests that pass while the UI is broken.
Your turn

Test a loading list

Medium

Write a test that renders a list that loads asynchronously, waits for an item to appear, and asserts it is visible.

TSX · starter
import { render, screen } from '@testing-library/react'
import LessonList from './LessonList'

test('shows lessons after loading', async () => {
  render(<LessonList />)
})
Check your understanding before moving on.
Knowledge checkQuestion 1

Which query waits for an element to appear asynchronously?

Select an answer to continue
Study tools

Make it yours.

Keep a private note for this lesson, then turn the key ideas into flashcards you can revisit.

Sign in to save notes and flashcards.Both are private to your account and stay with your progress.
Sign in
Finished reading?

Lock in what you just learned.

Mark this lesson complete to save your place and move one step closer to the certificate.

Next: Components and JSX
Lesson 5 of 300 · 0% of course