React Testing Library
Master React Testing Library for testing React components from a user's perspective. This is a foundational concept in component-based UI development that professional developers rely on daily. The explanations below are written to be beginner-friendly while covering the depth and nuance that comes from real-world React experience. Take your time with each section and practice the examples
90 min•By Priygop Team•Last updated: Feb 2026
What is React Testing Library?
React Testing Library is a very light-weight solution for testing React components. It provides light utility functions on top of react-dom and react-dom/test-utils, in a way that encourages better testing practices.
Basic Component Testing
Example
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
// Component to test
function Counter() {
const [count, setCount] = React.useState(0);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
</div>
);
}
// Test file: Counter.test.js
describe('Counter', () => {
test('renders counter with initial value', () => {
render(<Counter />);
expect(screen.getByText('Count: 0')).toBeInTheDocument();
expect(screen.getByText('Increment')).toBeInTheDocument();
expect(screen.getByText('Decrement')).toBeInTheDocument();
});
test('increments counter when increment button is clicked', () => {
render(<Counter />);
const incrementButton = screen.getByText('Increment');
fireEvent.click(incrementButton);
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
test('decrements counter when decrement button is clicked', () => {
render(<Counter />);
const decrementButton = screen.getByText('Decrement');
fireEvent.click(decrementButton);
expect(screen.getByText('Count: -1')).toBeInTheDocument();
});
});
// Testing forms
function LoginForm({ onSubmit }) {
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
const handleSubmit = (e) => {
e.preventDefault();
onSubmit({ email, password });
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
data-testid="email-input"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
data-testid="password-input"
/>
<button type="submit">Login</button>
</form>
);
}
describe('LoginForm', () => {
test('submits form with correct values', () => {
const mockOnSubmit = jest.fn();
render(<LoginForm onSubmit={mockOnSubmit} />);
const emailInput = screen.getByTestId('email-input');
const passwordInput = screen.getByTestId('password-input');
const submitButton = screen.getByText('Login');
fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
fireEvent.change(passwordInput, { target: { value: 'password123' } });
fireEvent.click(submitButton);
expect(mockOnSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123'
});
});
});Testing Async Operations
Example
// Testing async operations
import { render, screen, waitFor } from '@testing-library/react';
function UserProfile({ userId }) {
const [user, setUser] = React.useState(null);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(null);
React.useEffect(() => {
const fetchUser = async () => {
try {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>User not found</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
// Test with mocked fetch
describe('UserProfile', () => {
beforeEach(() => {
fetch.resetMocks();
});
test('renders user data after successful fetch', async () => {
const mockUser = { id: 1, name: 'John Doe', email: 'john@example.com' };
fetch.mockResponseOnce(JSON.stringify(mockUser));
render(<UserProfile userId={1} />);
expect(screen.getByText('Loading...')).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
expect(screen.getByText('john@example.com')).toBeInTheDocument();
});
test('renders error message on fetch failure', async () => {
fetch.mockRejectOnce(new Error('Failed to fetch'));
render(<UserProfile userId={1} />);
await waitFor(() => {
expect(screen.getByText('Error: Failed to fetch')).toBeInTheDocument();
});
});
});