Craft of code
Test levels
Unit, integration, contract, and end-to-end tests buy different confidence per minute. Pick with cost, not habit.
A useful strategy answers, for every change: which test level buys the most confidence per minute? The classic test pyramid says many fast unit tests, fewer integration tests, few end-to-end (E2E) tests. The intuition is cost. E2E tests are slow, brittle, and expensive to debug. Unit tests are cheap to run thousands of times.
Kent C. Dodds' testing trophy (popular in JS frontends) pushes more weight toward integration tests that exercise several real modules together, with static analysis at the base and fewer pure unit tests of implementation details. Backend services often land in between: lots of focused unit tests for pure logic, solid integration tests against real Postgres or Redis in CI, and a thin E2E smoke layer.
Walking through the cost
| Level | Speaks to | Typical runtime | Brittle when |
|---|---|---|---|
| Unit | Pure functions, domain rules | ms | You mock away the interesting parts |
| Integration | Modules + real DB or queue | 0.1–2 s | Shared mutable fixtures, order dependence |
| Contract | API shapes between services | sub-second–seconds | Schemas drift without consumer checks |
| E2E | Full user path in browser or API | seconds–minutes | Selectors, timing, environments |
Trade-off
E2E catches "we forgot to wire the button." Integration catches "the SQL join is wrong" without waiting on a browser. Default: protect critical money and auth paths with a handful of E2E smokes; put most confidence in integration tests against real dependencies (Testcontainers, docker-compose, or ephemeral DBs in CI).
Google's SMURF framing — speed, maintainability, utilization, reliability, fidelity — is the checklist when the pyramid slogan stops helping. An integration test that is flaky and slow can be worse than no test.
The mix for a money path is usually: unit-test the pure math, integration-test the persist against a real DB, one E2E smoke for wiring. See testing in production for the checkout walk-through.
Common mistakes
- The ice cream cone. One giant E2E suite as the only safety net.
- Unit tests that mock away the interesting parts. You proved the mock, not the SQL.
- Forty browser tests for one checkout. One smoke plus integration is enough for most paths.
- Treating the pyramid as a religion. SMURF when a test is slow and flaky.
Check yourself
-
The SQL join is wrong. Which level should have caught it? An integration test against a real database, not a mocked repository.
-
Why keep only a handful of E2E smokes on checkout? E2E is slow and brittle. Use it for wiring; put math and persistence lower in the pyramid.
Key takeaways
- Pick the level by confidence per minute, not habit.
- Pyramid and trophy are cost models. SMURF when they stop helping.
- Most backend confidence belongs in unit tests of pure logic plus integration against real dependencies.
- A thin E2E layer protects money and auth paths.
Further learning
Kent C. Dodds on the trophy mindset.
Next, choose doubles in test doubles.
Related
Testing strategy
What to test, at which level, and how to keep a suite fast, reliable, and worth the maintenance — from pyramid trade-offs to production safety nets.
Test doubles
A stub returns canned data. A fake is a small working stand-in. A mock also checks how it was called. Prefer fakes and real dependencies.
Continuous integration and the software supply chain
Fast, trustworthy pipelines - build, test, artifact, scan and sign - plus DORA metrics and supply-chain controls that keep trunk green.