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 component
  • screen: queries the DOM
  • fireEvent or userEvent: simulates user actions
  • expect: 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 over fireEvent for real interaction simulation
  • Use jest-dom matchers like toBeInTheDocument() or toHaveTextContent()

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