Master the art of testing React components with Jest and React Testing Library for reliable, maintainable test suites.
Things You'll Need
- React project set up
- Basic knowledge of React components
- Node.js and npm installed
Steps
Follow these 8 steps to complete this guide
Install Testing Dependencies
Set up Jest and React Testing Library in your project.
# Install Jest and React Testing Library
npm install -D @testing-library/react @testing-library/jest-dom
npm install -D @testing-library/user-event jest-environment-jsdom
# Install Jest (if not already included)
npm install -D jest Configure Jest
Create a Jest configuration file to set up the testing environment for React.
// jest.config.js
export default {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
moduleNameMapper: {
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
},
}; Create Setup File
Set up Jest DOM matchers for better assertions.
// jest.setup.js
import '@testing-library/jest-dom'; Write Your First Test
Create a simple test for a React component. Follow the Arrange-Act-Assert pattern.
// Button.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';
describe('Button', () => {
it('renders with the correct label', () => {
// Arrange
render(<Button label="Click me" onClick={() => {}} />);
// Assert
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
});
it('calls onClick when clicked', async () => {
// Arrange
const handleClick = jest.fn();
const user = userEvent.setup();
render(<Button label="Click me" onClick={handleClick} />);
// Act
await user.click(screen.getByRole('button'));
// Assert
expect(handleClick).toHaveBeenCalledTimes(1);
});
}); Tips
- Use describe blocks to group related tests
- Use screen.getByRole for more accessible queries
- userEvent is more realistic than fireEvent
Test Component State
Learn how to test components that manage state.
// Counter.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Counter } from './Counter';
describe('Counter', () => {
it('increments count when button is clicked', async () => {
const user = userEvent.setup();
render(<Counter />);
const button = screen.getByRole('button', { name: /increment/i });
const count = screen.getByText(/count: 0/i);
await user.click(button);
expect(screen.getByText(/count: 1/i)).toBeInTheDocument();
});
}); Test Async Operations
Use findBy queries and waitFor for testing asynchronous behavior.
// UserList.test.tsx
import { render, screen, waitFor } from '@testing-library/react';
import { UserList } from './UserList';
describe('UserList', () => {
it('displays users after loading', async () => {
render(<UserList />);
// Check loading state
expect(screen.getByText(/loading/i)).toBeInTheDocument();
// Wait for users to appear
const user = await screen.findByText(/john doe/i);
expect(user).toBeInTheDocument();
// Loading should be gone
expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();
});
}); Tips
- Use findBy for elements that will appear async
- Use waitFor when you need to wait for an assertion
- Mock API calls to avoid network requests in tests
Mock API Calls
Mock fetch or axios calls to test components in isolation.
// UserList.test.tsx
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve([
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
]),
})
) as jest.Mock;
describe('UserList', () => {
beforeEach(() => {
(fetch as jest.Mock).mockClear();
});
it('fetches and displays users', async () => {
render(<UserList />);
await waitFor(() => {
expect(screen.getByText(/john doe/i)).toBeInTheDocument();
});
expect(fetch).toHaveBeenCalledTimes(1);
});
}); Run Your Tests
Execute your test suite and view the results.
# Run all tests
npm test
# Run tests in watch mode
npm test -- --watch
# Run tests with coverage
npm test -- --coverage Tips
- Aim for at least 80% code coverage
- Watch mode is great for TDD (Test-Driven Development)
- Use coverage reports to find untested code
Was this guide helpful?
Check out more step-by-step guides for development, deployment, debugging, and configuration.