React Testing Cheat Sheet
Why Test React Components?
Testing ensures your components:
- Render correctly
- Respond to user interactions
- Handle edge cases and failures
- Stay reliable as your app grows
Types of tests:
- Unit tests — test a single component or function
- Integration tests — test components working together
- End-to-end (E2E) — test full user flows (not covered here)
Tools of the Trade
Tool | Purpose |
---|---|
Jest | Test runner + assertion library |
React Testing Library (RTL) | User-focused component testing |
Vitest (optional) | Fast alternative to Jest (Vite projects) |
Install basics:
npm install --save-dev jest @testing-library/react @testing-library/jest-dom
A Simple Test Example
// Counter.js
function Counter() {
const [count, setCount] = React.useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
</div>
);
}
// Counter.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
test('increments count when button is clicked', () => {
render(<Counter />);
fireEvent.click(screen.getByText(/increment/i));
expect(screen.getByText(/count: 1/i)).toBeInTheDocument();
});
RTL Essentials
render()
: mounts your componentscreen
: queries the DOMfireEvent
oruserEvent
: simulates user actionsexpect
: makes assertions
Useful queries:
screen.getByText(/submit/i); // find visible text
screen.getByRole('button'); // semantic role
screen.queryByTestId('header'); // optional fallback
Best Practices
- Test what the user sees, not the implementation
- Use descriptive queries (
getByRole
,getByLabelText
) - Avoid testing internal state or props directly
- Prefer
userEvent
overfireEvent
for real interaction simulation - Use
jest-dom
matchers liketoBeInTheDocument()
ortoHaveTextContent()
Testing Forms and Events
userEvent.type(screen.getByLabelText(/email/i), 'test@example.com');
userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(screen.getByText(/submitted/i)).toBeVisible();
Use userEvent
for typing, clicking, tabbing — it simulates real user behavior more accurately.
Snapshot Testing: Use with Caution
import { render } from '@testing-library/react';
import Component from './Component';
test('matches snapshot', () => {
const { asFragment } = render(<Component />);
expect(asFragment()).toMatchSnapshot();
});
- Snapshots catch accidental UI changes
- But: easy to misuse — don't rely on them for complex behavior
Coverage and Debugging
- Run tests:
npm test
- Coverage report:
npm test -- --coverage
- Debug a test:
screen.debug()
or inspect rendered output - Use
--watch
mode for fast feedback