Skip to content
End To End Tester

The Testing Pyramid

Why cheap tests must outnumber expensive ones, what the pyramid actually claims, the trophy and honeycomb alternatives, and how to tell which shape you really have.

4 min read · updated 19 September 2026

The pyramid is a claim about cost, and everything else about it follows from that. Tests at the bottom are cheap to write, cheap to run and cheap to diagnose. Tests at the top are expensive on all three. Therefore you should have a lot of the first kind and not many of the second.

That is the whole argument. It is not about correctness, purity, or what a "real" unit test is.

#What each level costs

Unit Integration End-to-end
Runtime microseconds 10ms – 1s 1s – 60s
Setup none a container or two a deployed system
What a failure tells you the function the seam something is broken
Failure when nothing changed essentially never occasionally routinely
Cost to fix a false failure minutes an hour half a day

The last row is the one that decides suite survival. A flaky test at the unit level is a curiosity. A flaky test at the end-to-end level is a tax on every merge, and teams respond to it the same way every time: retries, then quarantine, then deletion.

#What it actually prescribes

The pyramid is ordinal, not numerical. It says each layer should be smaller than the one beneath it. It does not say 70/20/10, and the people who quote that number are usually quoting a blog post quoting a misremembering of Mike Cohn's original diagram, which had no numbers on it.

A sensible reading:

  • Base — unit tests. Every branch of every non-trivial decision. Runs in seconds for the whole suite. If running them is not part of your inner loop, they are the wrong shape.
  • Middle — integration tests. Every seam where your code meets something it did not write: the database, the HTTP client, the message broker, the file system. One test per behaviour of the seam, not per method that crosses it.
  • Top — end-to-end tests. The handful of journeys that, if broken, mean you have no business. Sign up, sign in, buy the thing, get the receipt. Ten to forty of these for most products, not four hundred.

#How to tell what shape you actually have

Do not count test files. Count wall-clock minutes and failure diagnosis time, which is what the pyramid is really about.

bash
# The honest version of the question: where does the time go?
npm test -- --silent 2>&1 | tail -3          # unit
npx playwright test --reporter=line 2>&1 | tail -3   # end-to-end

If the second number is more than about four times the first, you do not have a pyramid regardless of what the file counts say. If more than one in fifty end-to-end runs fails for a reason that is not a real defect, you have an ice cream cone with extra steps.

#The alternatives, and what they get right

The testing trophy (Kent C. Dodds) fattens the middle: a small base of unit tests, a large body of integration tests, a few end-to-end. It is a reasonable modern reading, because the cost assumption underneath the pyramid has genuinely shifted. When the pyramid was drawn, "integration test" meant a shared test environment and a nightly run. With Testcontainers and in-process servers it now means 900 milliseconds. Cheaper middle, fatter middle. The pyramid's own logic gets you there.

The testing honeycomb (Spotify) makes the same move for a microservice estate, where nearly all the risk is in integration and almost none is in any single service's internal logic.

The ice cream cone is what you get by default, and it is worth understanding why: end-to-end tests are the only kind you can write without influencing the design of the code. A team that inherits a codebase, or one whose testers sit outside the engineering team, will produce a cone every time — not through ignorance but through access. See roles and writing testable code; the fix is structural.

#Where to put a given test

The question that resolves most arguments: what is the smallest thing that could be wrong here?

  • A discount is calculated as 15% instead of 20% → unit.
  • The discount is correct but stored in a DECIMAL(4,2) column and silently rounds → integration.
  • The discount is correct and persisted and never rendered on the summary page → end-to-end.

Each of those is a real bug with a natural home. Writing the first as an end-to-end test is the most common mistake in the field: it will pass, it will be slow, and when it eventually fails you will not know which of the nine things it touches was at fault.

#One caveat worth stating plainly

The pyramid describes a steady state, not a starting position. A team with no tests at all and a system in production should usually write a handful of end-to-end journeys first, because those are the tests that tell you whether the refactoring you are about to do broke anything. Build the base afterwards, under the safety net. Starting at the bottom of the pyramid is correct advice for a codebase you are writing and bad advice for one you are rescuing.

Common questions

What is the ideal ratio of unit to integration to end-to-end tests?
There isn't one. The commonly quoted 70/20/10 is a rule of thumb, not a finding. What the pyramid actually claims is ordinal — that each level should have fewer tests than the one below it — and the right numbers depend on how much of your risk lives in integration. A thin service that mostly moves data between a queue and a database legitimately has more integration tests than unit tests.
Is the testing pyramid outdated?
The shape is not, but two of its assumptions are. When it was proposed, integration tests were slow and awkward because spinning up real dependencies was slow and awkward; Testcontainers and in-process HTTP servers have made mid-level tests dramatically cheaper. The pyramid's cost argument still holds — it is just that the cost of the middle layer dropped, which is why the "testing trophy" shape is a reasonable modern reading of the same principle.
What is the ice cream cone anti-pattern?
The pyramid inverted — a large number of end-to-end and manual tests sitting on a thin layer of unit tests. It happens naturally, because end-to-end tests are the easiest kind to write when you do not control the code's design. The symptom is a suite that takes hours, fails unpredictably, and tells you that "checkout is broken" without telling you why.

Runnable samples for this page

last test results ↗
  • TypeScripttypescript/src/testing-levels/testing-pyramid

Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.

Was this page useful?