Testing Pyramid Explained
The testing pyramid, introduced by Mike Cohn, is the single most important architectural concept in test automation. It defines how many tests of each type you should write, why the distribution matters, and what goes wrong when you ignore it. Every SDET interview covers this topic.
The Three Layers
- Unit Tests (Base — 70%): Test individual functions/methods in isolation. Fastest, cheapest, most numerous. Written by developers, often alongside code.
- Integration Tests (Middle — 20%): Test how components interact — API + database, service + service. Medium speed, moderate cost.
- E2E Tests (Top — 10%): Test complete user journeys through the UI. Slowest, most costly, most brittle. Only for the most critical user flows.
The Testing Pyramid in Code
// ══════════════════════════════════════════════════════════════
// LAYER 1: UNIT TESTS (70%) — Fast, isolated, many
// ══════════════════════════════════════════════════════════════
// Tests a single pure function in complete isolation
function calculateOrderTotal(items, taxRate) {
const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
return subtotal + (subtotal * taxRate);
}
test("calculateOrderTotal - correct total with tax", () => {
const items = [{ price: 10, qty: 2 }, { price: 5, qty: 1 }];
expect(calculateOrderTotal(items, 0.1)).toBe(27.5); // $25 + 10% tax
});
// Runs in: 1ms | No network, no DB, no browser
// ══════════════════════════════════════════════════════════════
// LAYER 2: INTEGRATION TESTS (20%) — Medium speed, real deps
// ══════════════════════════════════════════════════════════════
// Tests the API endpoint with a real (test) database
test("POST /orders - creates order and returns 201", async () => {
const response = await request(app)
.post("/orders")
.set("Authorization", `Bearer ${testUserToken}`)
.send({ items: [{ productId: "p1", qty: 2 }] });
expect(response.status).toBe(201);
expect(response.body.orderId).toBeDefined();
// Verify it's actually in the database
const order = await db.orders.findById(response.body.orderId);
expect(order).not.toBeNull();
expect(order.items).toHaveLength(1);
});
// Runs in: 200ms | Uses test DB, real HTTP stack
// ══════════════════════════════════════════════════════════════
// LAYER 3: E2E TESTS (10%) — Slow, holistic, critical paths only
// ══════════════════════════════════════════════════════════════
test("User completes full purchase flow", async ({ page }) => {
await page.goto("/shop");
await page.click('[data-testid="add-to-cart-btn"]');
await page.click('[data-testid="checkout-btn"]');
await page.fill("#card-number", "4111111111111111");
await page.fill("#expiry", "12/26");
await page.fill("#cvv", "123");
await page.click('[data-testid="place-order-btn"]');
await expect(page.locator(".order-confirmation")).toBeVisible();
});
// Runs in: 8s | Requires browser, full stack, payment sandboxThe Anti-Patterns: Ice Cream Cone & Diamond
- Ice Cream Cone (avoid): Heavy UI/E2E tests at the top, few unit tests at the bottom. Slow, expensive, flaky — most teams end up here by accident.
- Diamond (wrong): Too many integration tests. Fragile to API changes, slow feedback loops.
- Correct Pyramid: Lots of unit tests → some integration tests → few E2E tests. Fast feedback, high confidence, manageable cost.
- Rule of thumb for a new feature: Write 5-10 unit tests, 2-3 integration tests, 1 E2E test for the critical happy path.
Common Mistakes
- Building an ice-cream cone — writing E2E tests for everything because 'they test the real system'. They do, but at 100x the cost.
- No unit tests — relying solely on integration/E2E tests means bugs take minutes, not milliseconds, to detect.
- Flaky E2E tests — one unreliable E2E test can block the entire CI pipeline. Keep E2E tests lean and deterministic.
- Mocking everything in integration tests — integration tests with all dependencies mocked become unit tests in disguise and lose their value.
Tip
Tip
Practice Testing Pyramid Explained in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
70% unit, 20% integration, 10% E2E.
Practice Task
Note
Practice Task — (1) Write a working example of Testing Pyramid Explained from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Testing Pyramid Explained is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready software testing code.
Key Takeaways
- The testing pyramid, introduced by Mike Cohn, is the single most important architectural concept in test automation.
- Unit Tests (Base — 70%): Test individual functions/methods in isolation. Fastest, cheapest, most numerous. Written by developers, often alongside code.
- Integration Tests (Middle — 20%): Test how components interact — API + database, service + service. Medium speed, moderate cost.
- E2E Tests (Top — 10%): Test complete user journeys through the UI. Slowest, most costly, most brittle. Only for the most critical user flows.