Skip to main content

The Test Pyramid

The Test Pyramid guides teams to invest most in fast unit tests, fewer integration tests, and only a handful of end-to-end tests. This balance delivers quick, reliable feedback and keeps test suites maintainable as systems and migrations grow.

Organization
Martin Fowler
Published
May 1, 2012

Best Practice: The Test Pyramid

The Test Pyramid, popularized by Mike Cohn and refined by Martin Fowler, is a model for distributing automated tests across layers. It recommends a wide base of fast unit tests, a thinner middle layer of integration (service) tests, and a small tip of slow end-to-end (UI) tests. The shape reflects a trade-off: tests near the base run in milliseconds and pinpoint failures precisely, while tests near the tip are slower, more brittle, and harder to debug. Balancing the layers gives fast feedback and reliable signal without an unmaintainable test suite.

Step-by-Step Implementation Guidance

  1. Inventory your current tests and classify each as unit, integration, or end-to-end.
  2. Set target proportions (a common heuristic is roughly 70/20/10) and compare against reality.
  3. Push logic-heavy assertions down to unit tests, which should not touch the network, disk, or database.
  4. Use integration tests to verify boundaries: database queries, HTTP handlers, message consumers.
  5. Reserve end-to-end tests for a few critical user journeys only.
  6. Add the test layers to CI so unit tests gate every commit and slower tiers run on merge or schedule.
  7. Track suite runtime and flake rate, and rebalance when the pyramid inverts into an "ice-cream cone."

Common Mistakes Teams Make When Ignoring This Practice

  • Building an inverted pyramid dominated by slow, flaky UI tests.
  • Writing integration tests for logic that a unit test would cover faster.
  • Mocking so heavily in unit tests that they verify the mocks, not the code.
  • Letting end-to-end suites grow until CI takes hours and developers stop trusting it.
  • Treating coverage percentage as the goal rather than fast, reliable feedback.

Tools and Techniques That Support This Practice

  • Unit frameworks: JUnit, pytest, Jest, Vitest, Go testing, NUnit.
  • Integration testing: Testcontainers, Spring Boot Test, Supertest, WireMock.
  • End-to-end: Playwright, Cypress, Selenium.
  • Coverage and reporting: JaCoCo, Coverage.py, Istanbul.
  • CI orchestration: GitHub Actions, GitLab CI, Jenkins to stage the tiers.

How This Practice Applies to Different Migration Types

  • Cloud Migration: Keep unit tests cloud-agnostic; add integration tests against managed services in a staging account.
  • Database Migration: Use integration tests with Testcontainers to validate schema and query behavior on the new engine.
  • SaaS Migration: Lean on contract and integration tests at the API boundary; keep a few end-to-end checks for key flows.
  • Codebase Migration: Build a unit-test safety net before refactoring so behavior is locked in at the base layer.

Checklist

  • Tests are classified into unit, integration, and end-to-end layers.
  • Unit tests form the majority and run without external dependencies.
  • Integration tests cover database, API, and messaging boundaries.
  • End-to-end tests are limited to critical user journeys.
  • CI gates commits on fast tests and schedules slow tiers.
  • Suite runtime and flake rate are tracked and reviewed.
  • The pyramid is rebalanced when it starts to invert.