Testing isn't just about finding bugs; it's about documenting how your code should work and preventing regressions.
The Philosophy: Test Behavior, Not Implementation
Don't test that "the state variable is true". Test that "the success message is visible".
Setting Up
npm install --save-dev @testing-library/react jest
Writing Your First Test
// Button.js
export default function Button({ onClick, children }) {
return ;
}
// Button.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
test('calls onClick when clicked', () => {
const handleClick = jest.fn();
render();
const button = screen.getByText(/click me/i);
fireEvent.click(button);
expect(handleClick).toHaveBeenCalledTimes(1);
});
Testing Asynchronous Code
test('loads items from API', async () => {
render( );
// Check loading state
expect(screen.getByText(/loading/i)).toBeInTheDocument();
// Wait for items
const item = await screen.findByText(/item 1/i);
expect(item).toBeInTheDocument();
});
Best Practices
- Use
screen.getByRolewhenever possible (accessible). - Avoid testing internal state directly.
- Mock API calls with MSW (Mock Service Worker).