Testing React
| Tool |
Function |
| Jest |
Test runner + assertions |
| React Testing Library |
Rendering & interaction |
| Cypress / Playwright |
End-to-end |
npm install --save-dev @testing-library/react @testing-library/jest-dom
Testing Components
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import Greeting from './Greeting';
test('displays greeting', () => {
render(<Greeting name="Mike" />);
expect(screen.getByText('Hello, Mike!')).toBeInTheDocument();
});
Testing Events
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
test('counter increments', () => {
render(<Counter />);
fireEvent.click(screen.getByRole('button', { name: /increment/i }));
expect(screen.getByText('1')).toBeInTheDocument();
});
Testing Async
import { render, screen, waitFor } from '@testing-library/react';
test('loads user data', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({ json: () => Promise.resolve({ name: 'John' }) })
);
render(<UserProfile userId={1} />);
await waitFor(() => expect(screen.getByText('John')).toBeInTheDocument());
});
Mocking APIs
beforeEach(() => {
fetchUser.mockResolvedValue({ name: 'Mike', email: '[email protected]' });
});
afterEach(() => jest.clearAllMocks());
Best Practices
| Practice |
Reason |
| Test behavior, not implementation |
Safe to refactor |
Use getByRole |
Accessibility-first |
| Mock in moderation |
Don't mock too deeply |
| Test one thing per test |
Easy to debug |
Good tests reduce bugs when refactoring. Next chapter: Building & Deployment.