Lesson 2 of 9 · 16m
The Testing Pyramid, Done Pragmatically
Unit tests for logic, integration tests for seams, end-to-end tests for the golden path — and why the shape matters.
The pyramid says: many fast unit tests at the bottom, a narrower band of integration tests in the middle, and a few slow end-to-end tests at the top. Teams get the shape wrong more than the counts — they write E2E tests for everything and wonder why CI takes an hour and every failure is flaky.
Unit tests: the fast feedback loop
Test one function or class in isolation, in-process, no I/O. They run in milliseconds, they pinpoint exactly which line broke, and they are cheap enough to write in bulk. Pure logic — validation, parsing, calculation, state machines — should live here.
Integration tests: the seams
Test the boundary where your code meets the database, the HTTP client, the filesystem. These are slower — real Postgres or a real HTTP round-trip — and they catch the bugs unit tests cannot: wrong query shapes, serialization edge cases, transaction ordering. You need fewer of them, but the ones you need are load-bearing.
End-to-end: the golden path only
A browser-level test that exercises the real stack for the one path that matters most: "a member signs in and posts a thread." E2E is the most expensive to run and the flakiest to maintain — every layer above the unit has more ways to fail for the wrong reason. Spend it on the paths where "if this breaks, the product is broken."
Try it yourself — where a bug lives, in JS
// A unit-level bug: this function alone is wrong — nothing else involved:
function normalizeTag(s) { return s.trim().toLowerCase(); }
console.log(normalizeTag(" Python ")); // "python"
// An integration bug: wrong query shape / wrong endpoint — only shows at the seam.
// An E2E bug: only shows when the whole stack runs together.
console.log("Each level catches what the level below cannot.");The number that matters
Not coverage percentage — the ratio: roughly 70% unit, 25% integration, 5% E2E of your suite, tuned to your codebase. Invert it and CI becomes the bottleneck instead of the safety net.