The testing pyramid is the most repeated advice in software engineering, and the most frequently ignored. In practice, many codebases have the inverse: a thin layer of unit tests and a heavy E2E suite that takes 45 minutes to run. This is backwards, and it's expensive.
The test pyramid (Kent Beck, expanded by Martin Fowler) places fast, cheap unit tests at the base, integration tests in the middle, and E2E tests at the top. The rationale is economic: a unit test runs in milliseconds and pinpoints the exact failing line. An E2E test takes 2-5 seconds, requires a browser, a server, a database, and a test environment, and gives you a stack trace that might end at a network timeout.
Unit tests: test a single function, class, or module in isolation with all dependencies mocked or stubbed. They should be the majority of your test count — roughly 60-70% of total tests. Vitest and Jest run thousands of unit tests in under 10 seconds. The focus: pure functions, business logic, edge cases (empty arrays, null inputs, overflow values), and algorithm correctness. Don't unit test framework code.
Integration tests: test multiple units working together — a service method that hits a real database, a React component that renders with real API data (via MSW mocking), or a REST endpoint end-to-end through your middleware stack. These should be 20-30% of your test suite. They're slower than unit tests but faster than E2E, and they catch the contracts between modules that unit tests miss.
E2E tests: test complete user workflows in a real browser against a running application. Reserve these for critical paths: signup flow, payment checkout, core feature workflows. 5-15% of tests, targeting the workflows where a silent failure costs the most. E2E tests are expensive to write, maintain, and run — use them selectively.
The common dysfunction to avoid: teams that write 90% E2E tests because they don't trust unit tests. This is usually a symptom of untestable code (tight coupling, global state, no dependency injection). Fix the architecture before fixing the test ratio.
CI cost implication: a 1,000-test suite that's 70% unit/20% integration/10% E2E runs in under 5 minutes. The inverse (10% unit/20% integration/70% E2E) runs in 45-90 minutes and costs 10x more in CI minutes.