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.getByRole whenever possible (accessible).
  • Avoid testing internal state directly.
  • Mock API calls with MSW (Mock Service Worker).