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
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.
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.
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.
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