React Testing Library
01 / 03

Render, Screen & Queries

React Testing Library: Render, Screen & Queries

RTL encourages testing components the way users interact with them — through accessibility roles, labels, and visible text — rather than testing implementation details like component state or internal methods.

Setup & render

// Install
// npm install --save-dev @testing-library/react @testing-library/user-event @testing-library/jest-dom
// npm install --save-dev vitest jsdom @vitejs/plugin-react  (Vite projects)

// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
  plugins: [react()],
  test: { environment: 'jsdom', globals: true, setupFiles: './src/test/setup.ts' },
})

// src/test/setup.ts
import '@testing-library/jest-dom'

// Basic render
import { render, screen } from '@testing-library/react'
import { Button } from './Button'

test('renders button with label', () => {
  render(<Button onClick={jest.fn()}>Save</Button>)
  expect(screen.getByRole('button', { name: /save/i })).toBeInTheDocument()
})

Query Types & Priority

RTL has three query variants × multiple query strategies. Always prefer the most accessible query (role > label > text > testId).

Query variants:
  getBy*     — throws if not found or multiple found. Use for asserting element exists.
  queryBy*   — returns null if not found. Use to assert element does NOT exist.
  findBy*    — async, returns Promise. Use when element appears after async operation.
  getAllBy*  / queryAllBy* / findAllBy* — same but for multiple elements.

Query strategies (priority order — use highest available):
  ByRole          getByRole('button', { name: /save/i })     — most preferred (accessibility)
  ByLabelText     getByLabelText('Email address')            — form inputs
  ByPlaceholderText getByPlaceholderText('Search...')
  ByText          getByText(/hello world/i)                  — non-interactive elements
  ByDisplayValue  getByDisplayValue('Alice')                 — selected input value
  ByAltText       getByAltText('Profile photo')              — images
  ByTitle         getByTitle('Close')
  ByTestId        getByTestId('submit-btn')                  — last resort
// ByRole — most powerful query
// ARIA roles: button, link, heading, textbox, checkbox, radio,
//             combobox, listbox, option, img, dialog, alert, navigation, main

screen.getByRole('heading', { level: 1 })           // <h1>
screen.getByRole('textbox', { name: /email/i })     // <input> with label "Email"
screen.getByRole('button', { name: /submit/i })
screen.getByRole('checkbox', { name: /agree/i, checked: true })
screen.getByRole('combobox', { name: /country/i })  // <select>

// ByLabelText — recommended for form inputs
screen.getByLabelText('Email address')
screen.getByLabelText(/password/i)
// Works with: <label for>, aria-label, aria-labelledby, title

// ByText — for static content
screen.getByText('Hello, World!')
screen.getByText(/hello/i)                          // regex — case insensitive
screen.getByText('Price:', { exact: false })        // substring match

// queryBy — assert absence
expect(screen.queryByText('Error message')).not.toBeInTheDocument()

// getAllBy — multiple elements
const items = screen.getAllByRole('listitem')
expect(items).toHaveLength(3)

jest-dom Matchers

// @testing-library/jest-dom extends expect() with DOM-specific matchers
expect(element).toBeInTheDocument()
expect(element).not.toBeInTheDocument()
expect(element).toBeVisible()
expect(element).toBeDisabled()
expect(element).toBeEnabled()
expect(element).toBeChecked()
expect(element).toHaveFocus()

expect(element).toHaveValue('alice@example.com')
expect(element).toHaveDisplayValue('Option A')
expect(element).toHaveTextContent(/hello/i)
expect(element).toHaveTextContent('exact string')

expect(element).toHaveAttribute('aria-label', 'Close')
expect(element).toHaveAttribute('disabled')
expect(element).toHaveClass('btn-primary')
expect(element).toHaveStyle({ backgroundColor: 'red' })

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free