C.W.K.
Stream
Lesson 02 of 03 · published

The Pyramid (and Its Anti-Shapes)

~18 min · foundations, pyramid, test-strategy

Level 0Test Curious
0 XP0/32 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Fast tests at the bottom, slow tests at the top, and very few of the slow ones."

The Shape That Wins

Mike Cohn drew the testing pyramid in 2009 and the picture has aged better than most. The base is a wide layer of fast unit tests — hundreds of them, each running in milliseconds. Above that, a thinner band of integration tests — dozens. At the top, a handful of end-to-end tests covering critical flows.

The shape tracks three orthogonal axes — speed, cost, and fidelity. Unit tests are fast, cheap, low-fidelity (they don't simulate a real browser). E2E tests are slow, expensive, high-fidelity (they are a real browser). The pyramid says: spend most of your test budget where the cost is low, and top it off where the fidelity matters.

The Anti-Shapes

The ice-cream cone: lots of E2E, a few integration, almost no unit. Common in projects that started as "manual QA writes Selenium scripts." Result: a 45-minute CI pipeline that's still flaky.

The hourglass: lots of unit, lots of E2E, almost no integration. Looks fine on a diagram, falls apart in practice — most user-facing regressions hide in the middle layer (a unit passes, an E2E passes, but the API contract between two modules is broken).

The trophy (Kent C. Dodds): integration-heavy. This one isn't wrong — it's a deliberate tradeoff for projects where integration is the highest-value layer. But following it blindly because Kent said so is the same religion problem from the last lesson.

The shape isn't a rule — it's the result of weighing cost against value for YOUR project. A pure-data library has almost no E2E surface, so its pyramid is mostly base. A consumer SaaS where a broken checkout costs money has more E2E than a textbook pyramid. Size the shape to the pain, not the diagram.

What Maps to What

For a typical React + Next.js project:

  • Unit (Vitest): pure functions, reducers, utilities, custom hooks (with renderHook), small components rendered in jsdom/happy-dom.
  • Integration (Vitest + MSW): components that talk to mocked APIs, multi-component flows, route handlers tested in isolation.
  • E2E (Playwright): critical user journeys — sign up, log in, complete a purchase. The one or two flows where a regression actually costs you.

The Test Budget

You have a finite amount of time to write and maintain tests. The pyramid is one way to spend it; the trophy is another; "ad-hoc whatever feels useful" is a third. The bad outcome isn't choosing the wrong shape — it's not knowing you chose. Pick deliberately, write down why, and re-evaluate when the suite starts hurting (slow CI, flakes, low signal).

Code

Project layout — where each layer lives·text
src/
├── lib/
│   ├── format.ts
│   └── format.test.ts          ← unit (Vitest)
├── hooks/
│   ├── use-counter.ts
│   └── use-counter.test.ts     ← unit (Vitest + renderHook)
├── components/
│   ├── login-form.tsx
│   └── login-form.test.tsx     ← integration (Vitest + RTL + MSW)
├── app/
│   └── api/
│       └── auth/
│           ├── route.ts
│           └── route.test.ts   ← integration (Vitest)
└── ...

e2e/
├── auth.spec.ts                ← E2E (Playwright)
└── checkout.spec.ts            ← E2E (Playwright)

vitest.config.ts                ← unit + integration config
playwright.config.ts            ← E2E config
Typical CI timings — fast at base, slow at top·text
# What "fast at the bottom, slow at the top" actually looks like in CI

Unit tests (Vitest)         :  200 tests    2.3s
Integration tests (Vitest)  :   30 tests    8.1s
E2E tests (Playwright)      :    8 tests   47.0s    ← 4x the time for 25x fewer tests

Total: ~1 minute. Bug in unit logic? Caught at 2.3s.
Bug in the checkout flow? Caught at 47s — still fast enough to run on every PR.

External links

Exercise

Look at the project you're working on right now (or any open-source repo you know well). Count three numbers: how many unit tests, how many integration tests, how many E2E tests. Then draw the actual shape you've got. Is it a pyramid, a cone, an hourglass, or 'no tests at all'? Don't fix it yet — just see it. The seeing is half the work.
Hint
If you can't tell what category a test belongs in, that's information too. 'I have 50 tests but I don't know what shape they form' is the most common starting state.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.