Establishing the Test Pyramid in the Team Instead of Just Documenting It
AI generated
PASS
expect()
Testing · Test Pyramid · Team Culture · Code Review
Establishing the Test Pyramid in the Team Instead of Just Documenting It
From a wiki page to lived practice in code review

A testing strategy that only lives in a wiki does not change a team's behavior by itself. Only when pyramid thinking is anchored in code review, in the definition of done, and in a gradual migration of existing tests does a healthy balance between fast unit tests and expensive end to end tests actually hold up under deadline pressure.

16 min read Code Review · Definition of Done · Test Distribution Cypress · Playwright · PHPUnit · Jest

1. Why a documented testing strategy alone changes nothing

In almost every team that has ever engaged with the test pyramid, there is a Confluence article or a README file somewhere explaining the principle: many unit tests, fewer integration tests, few end to end tests. Yet the actual suite in practice often looks different, with hundreds of slow Cypress or Playwright tests and barely any unit tests for the actual business logic. The reason is rarely a lack of knowledge. Developers usually know the concept, but knowledge alone does not change behavior when there is no consequence to whether a pull request respects or ignores the pyramid.

A testing strategy that is only documented but never enforced or even made visible loses against the pressure of an approaching sprint deadline in daily work. Anyone who needs to secure a feature quickly under time pressure reaches for the tool that immediately produces understandable confidence, usually a browser test that simulates the full user flow. That feels safer than an isolated unit test with mocks, even though it is more expensive and fragile in the long run. Without a mechanism that influences this decision at the moment of writing, the pyramid remains a diagram in the wiki instead of a lived practice in the code.

2. The test pyramid: basics and common misconceptions

The basic principle of Mike Cohn's test pyramid is simple: unit tests form the wide base because they run fast, are isolated, and point exactly to the broken line of code on failure. Integration tests check the interplay of several components, for example a repository and a database, and form the middle layer. End to end tests simulate real user interactions across the entire application and form the narrow tip, because they are slow, expensive to maintain, and prone to flakiness. The pyramid shape therefore does not describe importance, but the recommended distribution based on execution speed and maintenance cost.

A common misconception is to treat the pyramid as a strict test-count target, for example seventy percent unit tests, twenty percent integration, ten percent end to end. Such numbers are rough orientation, not a mandate to be blindly fulfilled without context. What matters more is the underlying principle: every test should be written at the lowest possible level that reliably catches the relevant failure. The opposite of the pyramid, the so called ice cream cone anti pattern with many manual and E2E tests and barely any unit tests, usually does not emerge deliberately but creeps in through many individually reasonable seeming decisions.


<!-- phpunit.xml: separate test suites per pyramid layer enforce visible separation -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php">
    <testsuites>
        <testsuite name="unit">
            <directory>tests/Unit</directory>
        </testsuite>
        <testsuite name="integration">
            <directory>tests/Integration</directory>
        </testsuite>
        <testsuite name="e2e">
            <directory>tests/E2E</directory>
        </testsuite>
    </testsuites>
    <!-- CI runs unit and integration on every commit, e2e only before merge -->
</phpunit>

3. Anchoring pyramid thinking in code review

The most effective lever to bring the test pyramid from paper into everyday practice is code review. Instead of generally asking whether a test exists, a reviewer should specifically ask at which level a test was written and whether that level is appropriate. If a pull request adds a new Playwright test for a pure price calculation that requires no browser interaction at all, that is a legitimate reason for a review comment suggesting a unit test alternative. This moment in review is where culture actually forms, not the documentation in the wiki.

To help reviewers ask this question consistently, a short, concrete checklist as part of the pull request template helps, for example the explicit question of whether the fastest possible test was chosen that reliably covers the failure case. It is important that this rule is not applied dogmatically against every E2E test. A new checkout flow genuinely needs an end to end test, but the edge case check of a discount calculation within that flow belongs in a unit test next to it, not in the same browser test. Reviewers who consistently ask for the lowest sensible level shift the distribution pull request by pull request in the right direction.

4. Definition of done as a lever for test distribution

A definition of done that only requires "tests present" is too unspecific to protect the pyramid. More effective is a definition of done that explicitly distinguishes between levels: business logic needs unit test coverage, interaction with external systems needs an integration test, and only critical user flows justify an additional end to end test. A ticket is only considered done once tests exist at the right level, not once some test is green. This precision turns a vague statement of intent into a verifiable rule that both the developer and the reviewer can check against the ticket.

In the CI pipeline, this rule can additionally be supported technically by defining a budget for the runtime and number of E2E tests per pull request. If a change clearly exceeds this budget, it is a signal that too much logic is probably being tested through the browser instead of at a lower level. The following example shows how such a budget is made visible as part of the pipeline configuration instead of existing only informally in team conversation.


# .github/workflows/test-pyramid-gate.yml
name: Test Pyramid Gate

on: [pull_request]

jobs:
  pyramid-budget:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run unit and integration tests (must run on every commit)
        run: npm run test:unit && npm run test:integration

      - name: Count new E2E test files in this PR
        id: e2e_count
        run: |
          count=$(git diff --name-only origin/main...HEAD -- 'tests/e2e/**/*.spec.ts' | wc -l)
          echo "count=$count" >> "$GITHUB_OUTPUT"

      - name: Fail if E2E budget is exceeded without justification
        if: steps.e2e_count.outputs.count > 3
        run: |
          echo "This PR adds more than 3 new E2E tests."
          echo "Please confirm in the PR description why lower-level tests are insufficient."
          exit 1

5. The E2E trap: easier to reason about, costlier to maintain

A central, often underestimated reason for the ice cream cone distribution is a cognitive trap: an end to end test is easier to write, because it simply reproduces what a human would do on screen, click, input, verify. A good unit test, by contrast, requires that the unit under test can even be isolated, which often demands a restructuring with dependency injection or a clean separation of business logic and infrastructure. Anyone who shies away from that restructuring falls back on the browser test, which works around any architectural weakness because it treats the application as a black box.

The problem only shows up later: the E2E test runs slowly, reacts sensitively to timing issues and network latency, and a single broken selector change takes down dozens of tests at once. A unit test for the same edge case would run in milliseconds and would show exactly which calculation is wrong. The short term convenience of the E2E test becomes a long term maintenance burden for the entire suite. The following example shows the same test case once as an expensive browser test and once as a focused unit test for the same business rule.


// BAD: business rule tested only through the full browser flow
test('applies 10% discount for orders over 100 EUR', async ({ page }) => {
  await page.goto('/cart');
  await page.getByTestId('add-item').click();
  await page.getByTestId('qty-input').fill('5');
  await page.getByTestId('checkout-btn').click();
  await expect(page.getByTestId('discount-line')).toContainText('-10.00');
  // Slow, flaky under load, and unclear which rule actually broke
});

// GOOD: same business rule isolated and tested in milliseconds
import { calculateDiscount } from '../src/pricing/discount';

test('applies 10% discount for orders over 100 EUR', () => {
  const result = calculateDiscount({ subtotal: 150 });
  expect(result.discountAmount).toBe(15);
  expect(result.reason).toBe('volume_discount_10');
});
// The E2E flow itself still deserves one happy-path test, just not every rule

6. Measuring test distribution: metrics instead of gut feeling

Without numbers, the discussion about the test pyramid stays subjective, and every team member has their own gut feeling about whether the suite is healthy. A simple but effective script regularly counts how many test files exist per level and how long each level takes in the CI pipeline. These numbers, displayed as a trend over time, objectively show whether the distribution is improving or worsening, regardless of how individual decisions felt during review.

Besides the raw count, the flakiness rate per level is an equally important metric: a team that discovers that twenty percent of its E2E tests occasionally fail without reason, while unit tests are practically never flaky, has a concrete, data backed reason to move logic from the tip of the pyramid to the base. These metrics belong on a dashboard that is visibly discussed in the sprint review or retro, instead of gathering dust in a one time audit report that nobody opens again.


#!/usr/bin/env bash
# scripts/test-distribution.sh - report the current pyramid shape
set -euo pipefail

unit_count=$(find tests/Unit -name '*Test.php' | wc -l)
integration_count=$(find tests/Integration -name '*Test.php' | wc -l)
e2e_count=$(find tests/e2e -name '*.spec.ts' | wc -l)
total=$((unit_count + integration_count + e2e_count))

printf 'Unit:        %4d (%d%%)\n' "$unit_count" $((unit_count * 100 / total))
printf 'Integration: %4d (%d%%)\n' "$integration_count" $((integration_count * 100 / total))
printf 'E2E:         %4d (%d%%)\n' "$e2e_count" $((e2e_count * 100 / total))

# Fail the pipeline if the pyramid inverts into an ice cream cone
if [ "$e2e_count" -gt "$unit_count" ]; then
  echo "[WARN] More E2E tests than unit tests. Pyramid is inverted."
fi

7. Incremental migration from unhealthy to healthy

A team that inherits an existing suite with hundreds of E2E tests and barely any unit tests should never attempt to rewrite everything at once. Such an undertaking ties up weeks of capacity, delivers no functional value in the meantime, and in practice is almost always abandoned after the first urgent feature request. The more effective path follows the strangler principle: every new feature gets pyramid compliant coverage from the start, and existing code is only migrated when it is touched anyway, for example as part of a bugfix or an extension.

To keep this migration from drifting arbitrarily, a rough target ratio per quarter helps, adjusted gradually rather than set as an unrealistic immediate goal. A team currently at five percent unit test share should not set a direct target of seventy percent, but rather define realistic intermediate steps and track them visibly during sprint planning. It is particularly effective to add a focused unit test for the concrete root cause alongside every bugfix in an E2E heavy area, so migration and bugfixing share the same effort instead of being separate work packages.


{
  "test_pyramid_roadmap": {
    "current_quarter": "2026-Q3",
    "baseline": { "unit_pct": 12, "integration_pct": 18, "e2e_pct": 70 },
    "targets": [
      { "quarter": "2026-Q3", "unit_pct": 25, "integration_pct": 25, "e2e_pct": 50 },
      { "quarter": "2026-Q4", "unit_pct": 45, "integration_pct": 30, "e2e_pct": 25 },
      { "quarter": "2027-Q1", "unit_pct": 60, "integration_pct": 25, "e2e_pct": 15 }
    ],
    "migration_rule": "new_features_pyramid_compliant_by_default",
    "opportunistic_migration": "add_unit_test_when_touching_e2e_covered_code"
  }
}

8. Anchoring ownership and responsibility in the team

The test pyramid remains fragile as long as test responsibility is understood as the job of a separate QA role that adds tests at the end of development. A more sustainable model has every developer responsible for the tests of their own code, including the decision about which level a test belongs on. Pairing between experienced and newer team members while writing unit tests transfers practical skill faster than any documentation, because the decision for a test level is worked through live against concrete code.

A rotating role as test steward, not permanently tied to one person, additionally helps to make test debt visible in the retro regularly, without perceiving test quality as one individual's side task. It is important that this role does not absorb responsibility that should actually rest with the whole team, but only prepares the metrics from section six and puts them up for discussion. That way the test pyramid remains a shared concern of the team, not the duty of a single person that immediately dilutes again in their absence.

9. Test pyramid patterns compared directly

The following overview summarizes which concrete decisions favor an unhealthy test distribution and which alternative has proven more sustainable in practice for actually anchoring the pyramid in daily work.

Situation Unhealthy pattern Healthy pattern Benefit
New business logic Covered only through one E2E test Unit test plus one E2E test for the happy path Fast, precise feedback on regressions
Code review "Test exists" is enough of a criterion Explicit question about the right test level Distribution shifts PR by PR
Definition of done Vague wording without level reference Level specific test requirement per ticket type Verifiable, non negotiable rule
Reworking an existing suite Big-bang rewrite sprint Strangler migration on every touch No standstill, steady progress
Test ownership Separate QA role at the end of the pipeline Developer owns tests of their own code Test level decided directly while writing code

Mironsoft

Test pyramid audits, CI gates and test automation for Magento and Hyva teams

A test pyramid that actually works in daily practice?

We analyze your existing test suite, build code review checklists and CI gates that enforce pyramid thinking, and guide the gradual migration from E2E heavy suites to a healthy test distribution.

Test distribution audit

Build metrics on test levels, runtime and flakiness

CI gate setup

Anchor E2E budget, pyramid checks and definition of done

Migration support

Plan and execute a strangler migration of existing E2E heavy suites

10. Summary

Establishing the test pyramid in the team means translating it out of the wiki into concrete, daily decisions. Code review that specifically asks about the right test level, and a definition of done that distinguishes between unit, integration and E2E requirements, change a team's behavior more reliably than any well written documentation ever could. The E2E trap emerges because browser tests are easier to write in the short term than isolated unit tests, but they cause high long term maintenance costs through flakiness and long runtimes.

An existing unhealthy distribution cannot be fixed overnight, but through a consistent strangler migration, where new features are built pyramid compliant and existing code is migrated when touched, the distribution shifts noticeably over quarters. Objective metrics instead of gut feeling, visibly discussed in the sprint review, and shared responsibility across the whole team instead of an isolated QA role turn the test pyramid into a lived practice instead of a diagram in the wiki.

Establishing the Test Pyramid in the Team, The Essentials at a Glance

Code review as a lever

Explicitly ask about the lowest sensible test level, instead of only checking whether some test exists.

Definition of done

Level specific test requirements instead of vague wording make the rule verifiable.

Avoiding the E2E trap

Browser tests are more convenient short term but more expensive long term than clean unit tests.

Incremental migration

Strangler principle instead of big-bang rewrite, backed by metrics instead of gut feeling.

11. FAQ: Establishing the Test Pyramid in the Team

1What is the test pyramid and why is it more than a diagram?
Describes the recommended test distribution by speed and maintenance cost. Only works when translated into code review and definition of done, not as pure documentation.
2Why isn't a documented strategy in a wiki enough?
Knowledge alone does not change behavior without consequence. Under time pressure, teams reach for the tool that creates immediate confidence, usually an E2E test.
3How do I anchor pyramid thinking in code review?
Specifically ask at which level a test was written. A checklist in the PR template makes this question consistent.
4What belongs in a definition of done regarding tests?
Level specific requirements instead of vague wording: unit for logic, integration for system interplay, E2E only for critical flows.
5Why do teams prefer writing E2E tests instead of unit tests?
E2E tests are easier to write, good unit tests need isolable code with clean architecture, which teams often avoid under time pressure.
6How do I measure the current test distribution of a project?
A script counts test files per level and measures CI runtime and flakiness rate. Shown as a trend in the sprint review this objectively shows progress.
7How do I migrate an existing suite without rewriting everything?
Strangler principle: new features pyramid compliant, existing code migrated when it is touched anyway.
8Who is responsible for test distribution in the team?
Every developer for their own code. A rotating role can prepare metrics without carrying sole responsibility.
9What is the ice cream cone anti pattern?
The opposite of the pyramid: many E2E tests, barely any unit tests. Usually emerges gradually through many short term convenient individual decisions.
10Does a healthy test pyramid replace E2E tests entirely?
No. Critical flows like checkout still need E2E tests. The pyramid only shifts the distribution, not the complete removal of a level.