Why a pyramid?
Not all tests cost the same nor give the same confidence. The testing pyramid (Mike Cohn) is a heuristic for distributing effort: wide at the base, narrow at the top.
/\ E2E ← few: slow, fragile, high confidence
/ \
/----\ Integration ← some: several pieces together
/ \
/--------\ Unit ← many: fast, cheap, isolated
The three layers
- Unit (base, majority): test an isolated unit (a function, a class) without touching network, disk or database. They run in milliseconds, so you can have thousands and run them on every save. When they fail, they pinpoint precisely what broke.
- Integration (middle): check that several pieces collaborate well: an endpoint with its data layer, a component with its store. Slower than the unit tests, but they catch errors the unit tests don't see (contracts between modules).
- E2E (top, minority): exercise the whole application like a real user, from the browser down to the database. They give the most confidence ("this really works") but are slow and fragile.
The cost / speed / confidence axis
As you go up the pyramid, each test covers more system (more confidence that the product works) but in exchange it is slower, more expensive to write and more prone to fail for reasons unrelated to the bug (timing, network, data). That's why you want many cheap tests at the bottom and few expensive ones at the top: maximum confidence per minute of execution.
Antipattern: the ice cream cone (many E2E, few unit). The suite takes hours, fails intermittently and nobody trusts it.
Examples
A unit test: fast and isolated
function priceWithVat(net, vat) {
return Math.round(net * (1 + vat) * 100) / 100;
}
// The "test" checks a single unit, without network or DB:
console.log(priceWithVat(100, 0.21) === 121 ? "OK" : "FAIL");