Test Pyramid — Unit, Integration, E2E Testing Strategy (2026)
In this tutorial, you'll learn about Test Pyramid. We cover key concepts, practical examples, and best practices.
The test pyramid is a visual metaphor that describes the ideal distribution of automated tests: many fast, isolated unit tests at the bottom, fewer integration tests in the middle, and a small number of slow end-to-end tests at the top.
What You'll Learn
You'll understand the classic test pyramid, compare it with the testing trophy alternative by Kent C. Dodds, learn ideal ratios for each test type, know when to write which kind of test, and understand cost considerations for each approach.
Why the Test Pyramid Matters
Without a strategy, teams end up with an inverted pyramid — hundreds of slow, flaky E2E tests and almost no unit tests. This makes the test suite a bottleneck instead of a safety net. At DodaTech, Durga Antivirus Pro follows the pyramid: thousands of unit tests for scanning logic, hundreds of integration tests for database interactions, and dozens of E2E tests for critical user flows.
Testing Strategy Learning Path
flowchart LR A[Unit Testing Guide] --> B[Test Pyramid] B --> C[Testing Trophy] B --> D[CI/CD Testing Pipeline] style B fill:#f90,color:#fff
The Classic Test Pyramid
flowchart TD
subgraph Test Pyramid
direction TB
U[Unit Tests
70% — Many, fast, cheap] --> I[Integration Tests
20% — Medium count, moderate speed]
I --> E[E2E Tests
10% — Few, slow, expensive]
end
The pyramid has three layers:
Unit Tests (Bottom)
Characteristics: Run in milliseconds, no external dependencies, test one function or method in isolation.
Purpose: Catch logic errors, document expected behavior, enable safe refactoring.
test('calculateTotal applies tax correctly', () => {
const items = [{ price: 10 }, { price: 20 }];
expect(calculateTotal(items, 0.1)).toBe(33); // (10 + 20) * 1.1
});
Integration Tests (Middle)
Characteristics: Run in seconds, involve real or simulated dependencies (database, API), test component interaction.
Purpose: Catch mismatches between components — wrong data shapes, missing fields, incorrect error propagation.
test('createUser inserts into database and returns ID', async () => {
const user = await userService.createUser({ name: 'Alice', email: 'alice@test.com' });
const saved = await db.query('SELECT * FROM users WHERE id = $1', [user.id]);
expect(saved.rows[0].name).toBe('Alice');
});
End-to-End Tests (Top)
Characteristics: Run in minutes, involve a full running system, simulate real user behavior.
Purpose: Verify the entire system works from the user's perspective.
test('user can complete checkout flow', async ({ page }) => {
await page.goto('/products');
await page.click('[data-testid="add-to-cart"]');
await page.click('[data-testid="checkout"]');
await page.fill('[name="card"]', '4242424242424242');
await page.click('[data-testid="pay"]');
await expect(page.locator('.confirmation')).toBeVisible();
});
The Testing Trophy (Alternative)
Kent C. Dodds proposed the testing trophy as a modern alternative:
flowchart TD
subgraph Testing Trophy
direction TB
S[Static Analysis
TypeScript, ESLint] --> U[Unit Tests]
U --> I[Integration Tests]
I --> E[E2E Tests]
end
The trophy emphasizes integration tests as the most valuable layer. Key differences from the pyramid:
| Aspect | Classic Pyramid | Testing Trophy | |--------|----------------|----------------| | Emphasis | Unit tests | Integration tests | | Static analysis | Not included | Foundation layer | | Mocking | Encouraged | Discouraged | | Best for | Backend/services | Frontend/SPA | |
Ideal Ratios
The exact ratios depend on your application type:
| Application | Unit | Integration | E2E |
|---|---|---|---|
| Backend API | 70% | 20% | 10% |
| Frontend SPA | 30% | 50% | 20% |
| Library/Package | 90% | 10% | 0% |
| Microservice | 60% | 30% | 10% |
When to Write Each Type
Write Unit Tests When
- Testing pure business logic and algorithms
- Validating input/output of individual functions
- You need fast feedback during development
Write Integration Tests When
- Verifying database queries work correctly
- Testing HTTP endpoint responses
- Ensuring services communicate properly
- Checking file system operations
Write E2E Tests When
- Covering critical user journeys (login, checkout, signup)
- Testing across system boundaries
- Validating deployment and configuration
Cost Considerations
Each test type has a different cost profile:
Cost per test:
Unit: $1 (milliseconds, no setup)
Integration: $10 (seconds, some setup)
E2E: $100+ (minutes, full environment)
The pyramid's message: spend most of your budget on the fast, reliable tests.
Common Mistakes
1. Inverted Pyramid
More E2E tests than unit tests leads to a slow, flaky suite.
2. No Integration Tests
Unit tests + E2E tests miss the middle layer where most real bugs live.
3. Testing Everything at Every Level
Duplicate coverage doesn't add value. Don't unit-test what you E2E-test for the same scenario.
4. Mock-Heavy Integration Tests
If all dependencies are mocked, it's a unit test, not an integration test.
5. Ignoring Static Analysis
TypeScript and ESLint catch entire categories of errors before any test runs.
6. Golden Hammer
Using one test type for everything. Each type has a job.
7. No Test Classification
Without tagging or organizing tests by type, you can't selectively run the right layer.
Practice Questions
1. What are the three layers of the test pyramid? Unit tests (bottom), integration tests (middle), E2E tests (top).
2. What is the testing trophy and who proposed it? The testing trophy by Kent C. Dodds emphasizes integration tests as the most valuable layer, with static analysis as the foundation.
3. What is the ideal test ratio for a backend API? Approximately 70% unit, 20% integration, 10% E2E.
4. Why are E2E tests the most expensive? They require a full running environment, take minutes to run, and are more flaky due to browser automation and network dependencies.
5. Challenge: Analyze your current project's test distribution. Count unit, integration, and E2E tests. Calculate the percentages. Is your pyramid inverted? What needs to change?
Mini Project: Test Ratio Analyzer
function analyzeTestDistribution(tests) {
const total = tests.length;
const unit = tests.filter(t => t.type === 'unit').length;
const integration = tests.filter(t => t.type === 'integration').length;
const e2e = tests.filter(t => t.type === 'e2e').length;
return {
total,
unit: ((unit / total) * 100).toFixed(1) + '%',
integration: ((integration / total) * 100).toFixed(1) + '%',
e2e: ((e2e / total) * 100).toFixed(1) + '%',
recommendation: unit > integration + e2e
? 'Pyramid looks healthy'
: 'Consider adding more unit tests',
};
}
const myTests = [
{ type: 'unit' }, { type: 'unit' }, { type: 'unit' },
{ type: 'integration' },
{ type: 'e2e' },
];
console.log(analyzeTestDistribution(myTests));
Expected output:
{
total: 5,
unit: '60.0%',
integration: '20.0%',
e2e: '20.0%',
recommendation: 'Consider adding more unit tests'
}
FAQ
What's Next
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro