React Testing Fundamentals

Write confident, accessible component tests using React Testing Library, Vitest, and userEvent simulation.

TL;DR

  1. Test user interface components from the end-user perspective.
  2. Locate interactive accessible elements reliably using standard screen.getByRole selectors.
  3. Simulate authentic user clicks and keyboard typing with @testing-library/user-event.

Rendering and Screen Queries

    Render Component

    Mount component into virtual jsdom container.

    import { render } from '@testing-library/react';
    import { screen } from '@testing-library/react';
    
    test('renders greeting', () => {
      render(<Greeting name="Sam" />);
      const el = screen.getByText(/hello sam/i);
      expect(el).toBeInTheDocument();
    });
    Query by Accessible Role

    Find buttons, inputs, and headings by role.

    const btn = screen.getByRole('button', {
      name: /submit/i,
    });
    Assert Element Absence

    Use queryBy to confirm element does not exist.

    expect(screen.queryByRole('dialog')).toBeNull();

Accessible Query Hierarchy

    1. Queries Accessible to Everyone

    Priority 1: getByRole, getByLabelText, getByText.

    screen.getByRole('button', { name: 'Save' });
    screen.getByLabelText('Email address');
    2. Semantic Queries

    Priority 2: getByAltText for images, getByTitle.

    screen.getByAltText('Company logo');
    3. Test IDs Escape Hatch

    Priority 3: getByTestId only when semantics fail.

    screen.getByTestId('custom-canvas');

User Event Simulation

    User Event Setup

    Initialize user session before component render.

    const user = userEvent.setup();
    render(<Counter />);
    await user.click(screen.getByRole('button'));
    Simulate Typing Input

    Type characters realistically into text field.

    const input = screen.getByRole('textbox');
    await user.type(input, 'Hello World');
    expect(input).toHaveValue('Hello World');
    Keyboard Navigation Event

    Simulate Tab and Enter keyboard actions.

    await user.tab();
    await user.keyboard('{Enter}');

Testing Assertions

    Element Visibility Matchers

    Assert visibility and DOM presence.

    expect(btn).toBeVisible();
    expect(btn).toBeDisabled();
    CSS Class and Attribute Matchers

    Assert specific attributes on elements.

    expect(link).toHaveAttribute('href', '/login');
    Form Value Matchers

    Assert checkbox and input values.

    expect(checkbox).toBeChecked();

Tips

  1. Always use screen.getByRole as your primary query selector because it ensures your components are fully accessible to screen readers.
  2. Always initialize userEvent.setup() before rendering components to ensure realistic browser event dispatch sequences and state synchronization.

Warnings

  1. Avoid testing internal component state or private methods; tests should only interact with what users can see and touch.
  2. Do not use fireEvent for user interactions; prefer userEvent because it dispatches realistic browser events faithfully.

In Practice

FAQ