Concept
The Testing Pyramid
The Testing Pyramid is a conceptual framework that guides how many tests of each type you should write:
/\
/ \ E2E Tests (Few, Slow, High Cost)
/----\
/ \ Integration Tests (Medium count/speed)
/--------\
/ \ Unit Tests (Many, Instant, Low Cost)
/────────────\- Unit Tests (Base): Test isolated functions, classes, or pure components. They run instantly in Node.js and are cheap to write and run.
- Integration Tests (Middle): Test how components and modules interact together (e.g. testing an Express route with a mock database or rendering a React component with state managers).
- End-to-End (E2E) Tests (Peak): Test the entire application flow in real browsers (e.g., using Cypress or Playwright), loading database layers and APIs. They give high confidence but are slow, flaky, and expensive to execute.
The Testing Trophy (Modern Alternative)
Coined by Kent C. Dodds, the Testing Trophy model shifts the focus for modern web applications. In dynamic JS codebases, code patterns (like React components interacting with API networks) mean Integration Tests yield the highest return-on-investment (ROI):
- E2E (Lid): Critical paths only (checkout, authentication).
- Integration (Body): Main focus. Tests components together with network mock layers.
- Unit (Base): Helper utilities, maths, date parsers.
- Static (Roots): TypeScript compiler and ESLint rules checking code correctness on keystrokes.
Common Mistakes
1. Writing E2E tests for every minor boundary path
E2E tests take minutes to run. If you write E2E tests for minor input fields or formatting layouts, your pipeline will run for hours, stalling releases and inducing flakiness. Keep E2E focused on critical user workflows.
2. Testing implementation details instead of user behavior
Writing tests that assert on private component states (e.g. expect(wrapper.state('isOpen')).toBe(true)) makes tests break whenever you refactor variables, even if the user-facing functionality is untouched. Test what the user sees (behavioral testing).
Best Practices
- Write Integration-First: Focus your core testing effort on integration tests that mimic user interaction.
- Run Unit Tests on Commit: Hook up unit tests to local Git pre-commit hooks to catch typos instantly.
- Establish flaky-test quarantine: Immediately isolate tests that fail intermittently (flaky tests) to prevent developers from ignoring pipeline warnings.
