Testing Intro

Test components the way users use them with React Testing Library.

Testing gives you confidence to change code without breaking it. The standard toolkit is Vitest (or Jest) as the test runner plus React Testing Library (RTL).

The RTL philosophy

  • Query the UI the way a user would — by role, label or text — not by implementation details.
  • Interact via userEvent (clicks, typing).
  • Assert on what the user sees.

This keeps tests resilient: they pass as long as the behavior is right, even if you refactor the internals.

Example

Example · javascript
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { expect, test } from 'vitest';
import Counter from './Counter.jsx';

test('increments on click', async () => {
  render(<Counter />);
  const button = screen.getByRole('button');
  await userEvent.click(button);
  expect(button).toHaveTextContent('Clicked 1 times');
});

When to use it

  • A CI pipeline runs Vitest on every pull request to catch regressions in form validation and click-handler behaviour before code is merged.
  • A design system uses React Testing Library to verify that every button variant is accessible by role and that keyboard interactions work correctly.
  • A refactoring session replaces class components with hooks, using pre-existing RTL tests as a safety net to confirm the rendered output and user flows are unchanged.

More examples

Render and query by role

Renders a component and queries the output by visible text — the RTL way of testing what users actually see.

Example · jsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import Greeting from './Greeting';

describe('Greeting', () => {
  it('displays the user name', () => {
    render(<Greeting name="Alice" />);
    expect(screen.getByText('Hello, Alice!')).toBeInTheDocument();
  });
});

Simulate a user click

Uses userEvent to simulate a real user click and verifies the resulting state change in the rendered output.

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

it('increments count on button click', async () => {
  const user = userEvent.setup();
  render(<Counter />);
  await user.click(screen.getByRole('button', { name: '+' }));
  expect(screen.getByText('Count: 1')).toBeInTheDocument();
});

Test a controlled form input

Types into a controlled input via userEvent and asserts the value updates correctly with each keystroke.

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

it('updates the input as the user types', async () => {
  const user = userEvent.setup();
  render(<SearchBox />);
  const input = screen.getByRole('searchbox');
  await user.type(input, 'react hooks');
  expect(input).toHaveValue('react hooks');
});

Discussion

  • Be the first to comment on this lesson.