React Testing Library
Test React components with React Testing Library - user-centric queries, async interactions, mocking, and accessibility-aware testing.
skill: React Testing Library category: Developer Experience tags: [React Testing Library, Testing, Jest, Vitest, React] readingTime: 6
Test the component as the user sees it - not the implementation details. RTL's queries (getByRole, getByText) mirror how assistive technologies access the DOM. If a test is hard to write, it's often a signal the component is too complex - refactor the component, not the test.
Workflow
- Query by role and text, not by classRTL's getByRole mirrors how screen readers find elements - tests written this way break when accessibility regresses, not when CSS class names change.
- Simulate user interactions with userEventuserEvent simulates real browser events (mousedown, keydown, focus, change) - more realistic than fireEvent which fires single events.
- Handle async UI with waitForComponents that fetch data show loading states before rendering content. waitFor/findBy waits for async state updates.
- Mock API calls with MSWMock Service Worker intercepts network requests at the service worker level - more realistic than mocking fetch directly.
- Test custom hooksCustom hooks can be tested in isolation with renderHook - faster than wrapping in a component.
Output Format
RTL tests: getByRole queries, userEvent interactions, findBy for async, MSW for API mocking, and renderHook for custom hooks.
const user = userEvent.setup();
render(<Component />);
await user.click(screen.getByRole('button', { name: /submit/i }));
expect(await screen.findByText(/success/i)).toBeInTheDocument();Examples
Input
Output
Edge Cases & Gotchas
- getBy vs. queryBy vs. findBy - getBy throws if not found (for elements that must exist). queryBy returns null (for asserting absence). findBy returns a Promise (for async elements).
- act() warnings - Warning "not wrapped in act()" means state updates happen after the test finishes. Wrap the async action in await act(async () => { ... }) or use findBy which wraps automatically.
- Context providers in tests - Components that use React Router, Redux, or custom contexts fail without wrapping. Create a custom render() function that wraps with all providers.
- Flaky animation tests - CSS transitions cause timing-sensitive failures. Disable animations in tests: .mockImplementation(() => {}) on requestAnimationFrame or use jest.useFakeTimers().
When to Break These Rules
- Use Playwright or Cypress for E2E tests that cover full user journeys across pages - RTL tests are component-scoped.
- Use Storybook interaction tests for component testing within the visual development environment.
Reference Files
https://testing-library.com/docs/react-testing-library/introhttps://mswjs.io/