Common Testing Mistakes

+15 Mana ✨

Avoid these pitfalls to write better tests.

1. Testing Implementation Details

jsx
// ❌ Bad: Testing internal state
test('sets loading to true', () => {
  const { result } = renderHook(() => useState(false));
  // Testing useState itself, not behavior
});

// ✅ Good: Testing user-visible outcome
test('shows spinner while loading', async () => {
  render(<DataLoader />);
  await user.click(screen.getByText('Load'));
  expect(screen.getByRole('status')).toBeInTheDocument();
});

2. Using Wrong Query

jsx
// ❌ Bad: Using getBy for absent element
test('error not shown initially', () => {
  render(<Form />);
  expect(screen.getByText('Error')).not.toBeInTheDocument();
  // Throws! getBy doesn't return null
});

// ✅ Good: Use queryBy for absent elements
test('error not shown initially', () => {
  render(<Form />);
  expect(screen.queryByText('Error')).not.toBeInTheDocument();
});

3. Not Waiting for Async

jsx
// ❌ Bad: Not waiting for async update
test('shows data', () => {
  render(<AsyncComponent />);
  expect(screen.getByText('Data')).toBeInTheDocument();
  // Fails! Data hasn't loaded yet
});

// ✅ Good: Wait for element
test('shows data', async () => {
  render(<AsyncComponent />);
  expect(await screen.findByText('Data')).toBeInTheDocument();
});

4. Incorrect waitFor Usage

jsx
// ❌ Bad: Side effects in waitFor
await waitFor(() => {
  fireEvent.click(button); // Don't do this!
  expect(result).toBe(true);
});

// ✅ Good: Only assertions in waitFor
await user.click(button);
await waitFor(() => {
  expect(result).toBe(true);
});

5. Snapshot Overuse

jsx
// ❌ Bad: Large, fragile snapshot
test('renders correctly', () => {
  const { container } = render(<ComplexPage />);
  expect(container).toMatchSnapshot();
  // Breaks on any change, even valid ones
});

// ✅ Good: Targeted assertions
test('renders header with user name', () => {
  render(<ComplexPage user={{ name: 'John' }} />);
  expect(screen.getByRole('heading')).toHaveTextContent('Welcome, John');
});

6. Not Cleaning Up

jsx
// ❌ Bad: Global state leaking between tests
let mockData = [];

test('adds item', () => {
  mockData.push('item');
  // Next test starts with ['item']!
});

// ✅ Good: Reset in beforeEach/afterEach
beforeEach(() => {
  mockData = [];
});

7. Hardcoded Timeouts

jsx
// ❌ Bad: Arbitrary wait time
test('shows success', async () => {
  render(<Form />);
  await user.click(submitButton);
  await new Promise(r => setTimeout(r, 2000)); // Slow and flaky!
  expect(screen.getByText('Success')).toBeInTheDocument();
});

// ✅ Good: Wait for actual condition
test('shows success', async () => {
  render(<Form />);
  await user.click(submitButton);
  await screen.findByText('Success');
});
✓ Completed