A checkout feature needs three E2E tests, not thirty: one for the happy path, one for payment failure, one for out-of-stock. The other edge cases are cheaper and more stable as unit and API tests. This article explains the pyramid's three layers, what happens when teams invert it into an ice cream cone, and the modern variant (the testing trophy) that fits frontend-heavy applications.
The original model
Mike Cohn introduced the test pyramid in 2009. The original version had three layers:
/\
/ \
/ E2E \ ← few, slow, expensive
/--------\
/ Integration\ ← some
/--------------\
/ Unit Tests \ ← many, fast, cheap
/------------------\The shape matters: many at the bottom, fewer at the top. The pyramid.
Why the shape matters
If you invert the pyramid — many E2E tests, few unit tests — you get:
- A slow suite (E2E tests take 10-60x longer than unit tests)
- Flaky tests (more moving parts = more failure modes)
- Poor debugging information (an E2E failure tells you something is wrong; a unit test failure tells you exactly what)
- Expensive maintenance (UI changes break E2E tests; APIs change less often)
The classic anti-pattern is an ice cream cone: a tiny unit test layer, no integration layer, and a massive E2E suite that takes 45 minutes to run and fails randomly.
The modern interpretation
The original pyramid predates microservices, serverless, and frameworks like Playwright that make E2E tests significantly more reliable. The modern version acknowledges this:
/\
/ \
/ E2E \ ← smoke tests, critical paths
/--------\
/ API/Svc \ ← integration, contract tests
/--------------\
/ Component/Unit \ ← business logic, utilities
/--------------------\The specific proportions vary by team and product type. A CRUD API might have very few E2E tests and heavy API coverage. A React dashboard with complex UI might have more component tests and fewer unit tests.
The principle stays the same: lean on the cheaper, faster, more stable tests. Reserve E2E for the scenarios that can only be verified through the full stack.
Applying the pyramid to a Playwright project
What belongs in E2E tests:- Critical user journeys (login → checkout → confirmation)
- Cross-service integration (frontend + backend + database together)
- Browser-specific behavior (file upload, multi-tab, OAuth redirect)
- Smoke tests for production deploys
- Validation error messages (test them in unit tests of the validation logic)
- Every edge case of a complex algorithm (unit test)
- API responses in isolation (API test)
- All 15 permutations of a form (equivalence partition, pick 3-4 representative cases for E2E)
A concrete example
You're testing an order placement feature. The requirements:
1. User adds item to cart
2. User goes to checkout
3. User fills shipping info
4. User pays
5. Order confirmation shown
6. Email sent to user
Unit tests cover:- Price calculation logic (discounts, tax, shipping costs)
- Email template rendering
- Address validation function
- Payment amount rounding
- POST /orders creates database record
- POST /orders with invalid payment returns 422
- Order status transitions (pending → confirmed → shipped)
- Email service called with correct parameters
- Happy path: complete order from cart to confirmation (one test)
- Payment failure: card declined, error shown, order not created (one test)
- Out-of-stock: item unavailable, user redirected with message (one test)
That's it. Three E2E tests for an entire checkout feature. The unit and API tests cover the edge cases — E2E covers the flows that matter most to users.
The testing trophy (alternative model)
Kent C. Dodds proposed the testing trophy in 2018, which tweaks the pyramid for JavaScript frontends:
/\
/ \ ← E2E (few)
/----\
/ Integ \ ← integration (most)
/----------\
/ Unit \ ← unit (some)
/--------------\
/ Static \ ← types, linting (always)
/------------------\The key difference: integration tests at the top of the middle. For React/Vue/Next.js apps, "integration tests" means rendering components with their actual dependencies (real API calls to a test server, or a mocked-at-the-network-layer version). This is the React Testing Library philosophy: test how users interact with the component, not implementation details.
Both models are valid. The pyramid applies well to backend systems. The trophy fits frontend-heavy applications. Either beats the ice cream cone.
The rule of thumb
When you're about to write an E2E test, ask: what is this test actually verifying that couldn't be verified at a lower level?
If the answer is "the frontend and backend are connected and data flows correctly through the entire stack" — write the E2E test.
If the answer is "that the form validation shows an error for an empty email field" — that's a unit test for the validation function and a component test for the form. The E2E test would just be noise.
→ See also: Smoke Testing vs Regression Testing: What's the Difference? | Playwright Component Testing: Testing React/Vue Components in Isolation | Test Automation Best Practices That Actually Matter