Skip to content

Latest commit

 

History

History
89 lines (74 loc) · 2.96 KB

File metadata and controls

89 lines (74 loc) · 2.96 KB
technology React
domain frontend
level Senior/Architect
version 19+
tags
react
testing
best-practices
clean-code
tdd
architecture
vibe-coding
ai_role Senior React Testing Expert
last_updated 2026-04-05

🧪 React Testing Best Practices

⬆️ Back to Top

📖 Context & Scope

  • Primary Goal: Enforce highly reliable, fast, and deterministic testing patterns in React.
  • Target Tooling: Cursor, Windsurf, Antigravity.
  • Tech Stack Version: React 19+

Important

Strict Constraints for AI:

  • Always test user behavior, not implementation details.
  • Never mock excessive internal state or standard React hooks; rely on integration testing methodologies.

🚀 I. Component Testing Philosophy

🚨 1. Testing Implementation Details

Note

Context: Verifying component logic using tools like Enzyme or direct state inspection.

❌ Bad Practice

test('updates state on click', () => {
  const wrapper = shallow(<Counter />);
  wrapper.find('button').simulate('click');
  expect(wrapper.state().count).toBe(1);
});

⚠️ Problem

Testing internal state makes tests brittle. If you refactor a class component to a functional component with hooks, the test will break even if the UI remains identical, leading to high maintenance overhead.

✅ Best Practice

import { render, screen, fireEvent } from '@testing-library/react';

test('increments counter on click', () => {
  render(<Counter />);
  const button = screen.getByRole('button', { name: /increment/i });

  fireEvent.click(button);

  expect(screen.getByText('Count: 1')).toBeInTheDocument();
});

🚀 Solution

Test from the user's perspective using React Testing Library. Query by accessible roles and verify the DOM output. This guarantees that refactoring implementation details will not break your test suite.


⚙️ II. Asynchronous Operations

🚨 2. Unhandled Asynchronous Updates

Note

Context: Testing components that fetch data or have delayed rendering.

❌ Bad Practice

test('loads data', () => {
  render(<UserProfile id="1" />);
  // Assuming the API call takes time, this assertion runs immediately and fails.
  expect(screen.getByText('John Doe')).toBeInTheDocument();
});

⚠️ Problem

React components that perform async operations will throw "act" warnings and fail assertions if the test runner doesn't wait for the microtask queue to resolve and the DOM to update.

✅ Best Practice

import { render, screen } from '@testing-library/react';

test('loads data', async () => {
  render(<UserProfile id="1" />);

  const userName = await screen.findByText('John Doe');
  expect(userName).toBeInTheDocument();
});

🚀 Solution

Use async utilities like findBy* or waitFor from React Testing Library. These tools inherently wrap updates in act() and poll the DOM until the asynchronous state resolves, ensuring deterministic test execution.


⬆️ Back to Top