React Component Testing with Testing Library
Test behavior over implementation. Use Testing Library queries to interact with components as users would.
Overview
React Testing Library fundamentally shifts the paradigm from testing component internals to testing user-facing behavior. The guiding principle "The more your tests resemble the way your software is used, the more confidence they can give you" informs every API decision and best practice in the library. Traditional testing approaches often led to brittle tests that broke when implementation details changed, even when the component's behavior remained correct. Testing Library addresses this by providing queries that prioritize accessibility and user perspective over component internals. Queries like getByRole, getByLabelText, and getByPlaceholderText simulate how users actually interact with the interface through assistive technologies. The fireEvent and userEvent APIs simulate real user interactions including keyboard navigation, mouse clicks, and form inputs. userEvent is preferred in most cases as it provides more realistic simulation of user behavior, including proper timing between keystrokes and handling of special keys. The waitFor and findBy queries handle asynchronous operations naturally, waiting for elements to appear rather than relying on arbitrary timeouts. Testing Library integrates seamlessly with Jest and other test runners, and its companion library jest-dom provides custom Jest matchers for DOM elements. These matchers like toBeInTheDocument, toHaveValue, and toHaveAttribute make assertions more semantic and the error messages when tests fail much more actionable. The setupFilesAfterEnv configuration ensures these matchers are available in every test file without explicit imports.
Code Example
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LoginForm } from './LoginForm';
describe('LoginForm', () => {
const mockOnSubmit = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
it('submits the form with valid credentials', async () => {
const user = userEvent.setup();
render(<LoginForm onSubmit={mockOnSubmit} />);
await user.type(screen.getByLabelText(/email/i), 'developer@example.com');
await user.type(screen.getByLabelText(/password/i), 'securePassword123');
await user.click(screen.getByRole('button', { name: /sign in/i }));
expect(mockOnSubmit).toHaveBeenCalledWith({
email: 'developer@example.com',
password: 'securePassword123',
});
});
it('displays validation errors for invalid email', async () => {
const user = userEvent.setup();
render(<LoginForm onSubmit={mockOnSubmit} />);
await user.type(screen.getByLabelText(/email/i), 'invalid-email');
await user.click(screen.getByRole('button', { name: /sign in/i }));
expect(screen.getByText(/please enter a valid email/i)).toBeInTheDocument();
expect(mockOnSubmit).not.toHaveBeenCalled();
});
it('shows password requirements on focus', async () => {
render(<LoginForm onSubmit={mockOnSubmit} />);
const passwordInput = screen.getByLabelText(/password/i);
fireEvent.focus(passwordInput);
await waitFor(() => {
expect(screen.getByText(/password must contain/i)).toBeInTheDocument();
});
});
});More React Rules
React Server Components Data Fetching Patterns
Master async/await patterns in Server Components for efficient data loading and caching.
import { db } from '@/lib/db';
import { cache } from 'next/cache';
import { Suspense } from 'react';
import { Skeleton } from '@/components/ui/skeleto...React Performance Optimization Patterns
Use React.memo, useMemo, and useCallback correctly to prevent unnecessary re-renders.
import React, { useState, useMemo, useCallback, memo, useEffect } from 'react';
// Expensive calculation helper
function calculateExpensiveValue(item...React Form Handling with React Hook Form
Build performant forms with React Hook Form using uncontrolled components and validation.
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import DatePick...