--- id: wiki-2026-0508-test-automation-pyramid title: Test Automation Pyramid category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Test Pyramid, 테스트 피라미드, Cohn Pyramid] duplicate_of: none source_trust_level: A confidence_score: 0.92 verification_status: applied tags: [testing, architecture, pyramid, automation] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: Vitest/Playwright --- # Test Automation Pyramid ## 매 한 줄 > **"매 unit test 가 base, e2e 가 tip"**. Mike Cohn 의 2009 Succeeding with Agile 에서 origin — slow/expensive UI test 의 over-reliance 의 anti-pattern. 매 2026 modern stack 은 unit + integration + contract + e2e 의 4-layer 의 evolution. ## 매 핵심 ### 매 layers (bottom-up) - **Unit (70%)**: pure function, 단일 class, <10ms, in-memory. - **Integration (20%)**: DB, HTTP client, 매 real adapter, 100ms-1s. - **Contract (5-10%)**: provider/consumer 매 Pact / OpenAPI schema. - **E2E (5%)**: 매 full user journey, Playwright/Cypress, 10s+. ### 매 anti-shapes - **Ice cream cone**: 매 e2e heavy, unit 부족 — slow CI, flaky. - **Hourglass**: 매 unit + e2e 만, integration 의 gap — production bug 의 source. - **Cupcake**: manual test layer 의 추가 — automation 의 의미 상실. ### 매 응용 1. CI pipeline tier — unit on every commit, e2e on merge to main. 2. Bug 발견 시 lowest-level test 의 추가 (root cause level). 3. Test budget 분배 결정 (시간 / 신뢰도 trade-off). ## 💻 패턴 ### Vitest unit test ```typescript import { describe, it, expect } from 'vitest'; import { calculateDiscount } from './pricing'; describe('calculateDiscount', () => { it('applies 10% for VIP customer', () => { expect(calculateDiscount(100, 'VIP')).toBe(90); }); it('returns full price for standard customer', () => { expect(calculateDiscount(100, 'STANDARD')).toBe(100); }); }); ``` ### Integration test (real Postgres via testcontainers) ```typescript import { PostgreSqlContainer } from '@testcontainers/postgresql'; let container: StartedTestContainer; beforeAll(async () => { container = await new PostgreSqlContainer('postgres:16').start(); process.env.DATABASE_URL = container.getConnectionUri(); }); it('persists user', async () => { const repo = new UserRepository(); await repo.save({ id: '1', name: 'Alice' }); const found = await repo.findById('1'); expect(found?.name).toBe('Alice'); }); ``` ### Contract test (Pact consumer) ```typescript import { PactV3, MatchersV3 } from '@pact-foundation/pact'; const provider = new PactV3({ consumer: 'web', provider: 'api' }); provider .uponReceiving('a request for user') .withRequest({ method: 'GET', path: '/users/1' }) .willRespondWith({ status: 200, body: { id: MatchersV3.string('1'), name: MatchersV3.string('Alice') } }); ``` ### Playwright E2E ```typescript import { test, expect } from '@playwright/test'; test('user can checkout', async ({ page }) => { await page.goto('/products/42'); await page.getByRole('button', { name: 'Add to cart' }).click(); await page.goto('/cart'); await page.getByRole('button', { name: 'Checkout' }).click(); await expect(page).toHaveURL(/\/orders\/\w+/); }); ``` ### Test pyramid CI distribution (GitHub Actions) ```yaml jobs: unit: runs-on: ubuntu-latest steps: - run: pnpm test:unit # <30s, every push integration: needs: unit steps: - run: pnpm test:integration # <5min, every PR e2e: needs: integration if: github.ref == 'refs/heads/main' steps: - run: pnpm test:e2e # <20min, main only ``` ### Coverage budget enforcement ```typescript // vitest.config.ts export default { test: { coverage: { thresholds: { lines: 80, functions: 80, branches: 75, statements: 80, }, }, }, }; ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Pure logic, no I/O | Unit test | | DB query / external HTTP | Integration with testcontainers | | Microservice boundary | Contract test (Pact) | | Critical user journey | E2E (smoke only, 5-10 tests) | | Visual regression | Playwright trace + screenshot | **기본값**: 70/20/10 split (unit/integration/e2e), 매 contract test 는 microservice 환경에서만. ## 🔗 Graph - 부모: [[Test-Driven_Development]] · [[Testability_Architecture]] - 응용: [[Continuous Integration]] · [[CI_CD_Pipeline]] - Adjacent: [[Mock Objects (가짜 객체)]] · [[Property-Based Testing]] ## 🤖 LLM 활용 **언제**: test strategy 설계, CI tier 분배 결정, anti-shape 진단. **언제 X**: 매 specific test framework 의 deep API 질문 — 매 official docs 의 우선. ## ❌ 안티패턴 - **All e2e**: 매 30분 CI, flaky retry hell. - **No integration**: unit pass + production fail (mock drift). - **Test pyramid as goal**: 매 ratio 의 obsession — 매 actual confidence 가 metric. - **100% coverage**: meaningless — 매 critical path coverage 가 진짜. ## 🧪 검증 / 중복 - Verified (Mike Cohn, *Succeeding with Agile* 2009; Martin Fowler 2012 update). - 신뢰도 A. - 중복: [[Test Pyramid (테스트 피라미드)]] redirect. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — full canonical content with 4-layer modern pyramid |