[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
---
|
||||
id: quality-test-strategy
|
||||
title: Test Strategy — pyramid / trophy / ice cream
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [quality, testing, vibe-coding]
|
||||
tech_stack: { language: "any", applicable_to: ["Engineering"] }
|
||||
applied_in: []
|
||||
aliases: [test pyramid, test trophy, ice cream cone, integration heavy, Kent Dodds]
|
||||
---
|
||||
|
||||
# Test Strategy
|
||||
|
||||
> "어떤 test 가 어느 비율?" **Pyramid (옛), Trophy (modern), Ice cream cone (anti)**.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- Unit: 빠름, 격리.
|
||||
- Integration: 의존 포함.
|
||||
- E2E: 사용자 flow.
|
||||
- 매 type 의 trade-off.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### Test Pyramid (Mike Cohn)
|
||||
```
|
||||
/\ E2E (작음)
|
||||
/ \ Integration
|
||||
/____\
|
||||
/ \
|
||||
/ Unit \ (큰)
|
||||
/__________\
|
||||
```
|
||||
|
||||
→ 70% unit, 20% integration, 10% E2E.
|
||||
|
||||
### Test Trophy (Kent C. Dodds)
|
||||
```
|
||||
___
|
||||
/ \ E2E (작음)
|
||||
/_____\
|
||||
/ \ Integration (큰)
|
||||
/_________\
|
||||
\ /
|
||||
\ Unit/ (중간)
|
||||
\___/
|
||||
Static (lint, type) — 큰
|
||||
```
|
||||
|
||||
→ Integration 가 큰. Static 도 비.
|
||||
|
||||
### Ice cream cone (anti-pattern)
|
||||
```
|
||||
/\ Manual (큰)
|
||||
/__\ E2E (큰)
|
||||
/____\
|
||||
/ \ Integration
|
||||
/________\
|
||||
/ \
|
||||
/ Unit (작음) \
|
||||
/______________\
|
||||
```
|
||||
|
||||
→ Manual / E2E heavy. 느린, 깨짐.
|
||||
|
||||
### 매 type 의 cost
|
||||
```
|
||||
Unit:
|
||||
- ms
|
||||
- 매 commit
|
||||
- 빠른 feedback
|
||||
- Low confidence (mock heavy)
|
||||
|
||||
Integration:
|
||||
- sec
|
||||
- 매 PR
|
||||
- Real DB / external
|
||||
- Higher confidence
|
||||
|
||||
E2E:
|
||||
- min
|
||||
- 매 deploy
|
||||
- Full UI / network
|
||||
- Highest confidence + brittle
|
||||
```
|
||||
|
||||
### 권장 비율
|
||||
```
|
||||
Static (lint + type): 무한.
|
||||
Unit: 60-70% (간단 logic).
|
||||
Integration: 20-30% (DB / API).
|
||||
E2E: 5-10% (critical path).
|
||||
```
|
||||
|
||||
### Static
|
||||
```bash
|
||||
eslint src/
|
||||
tsc --noEmit
|
||||
prettier --check
|
||||
```
|
||||
|
||||
→ "Test 의 99% 가 catch 가능".
|
||||
|
||||
### Unit (격리)
|
||||
```ts
|
||||
test('add', () => {
|
||||
expect(add(2, 3)).toBe(5);
|
||||
});
|
||||
```
|
||||
|
||||
→ Mock everything. 빠름.
|
||||
|
||||
### Integration (real dep)
|
||||
```ts
|
||||
test('user creation', async () => {
|
||||
const user = await api.createUser({ email: 'a@x' });
|
||||
const found = await db.users.findOne({ id: user.id });
|
||||
expect(found).toBeDefined();
|
||||
});
|
||||
```
|
||||
|
||||
→ Real DB (testcontainer). 느린.
|
||||
|
||||
### E2E (Playwright)
|
||||
```ts
|
||||
test('login flow', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.fill('[name=email]', 'a@x');
|
||||
await page.fill('[name=password]', 'pw');
|
||||
await page.click('button[type=submit]');
|
||||
await expect(page).toHaveURL('/dashboard');
|
||||
});
|
||||
```
|
||||
|
||||
→ Real browser. 매우 느린.
|
||||
|
||||
### Mock 의 함정
|
||||
```ts
|
||||
// ❌ Mock 가 진실 안 반영
|
||||
const mockDB = { findUser: jest.fn().mockResolvedValue({ id: 1 }) };
|
||||
// → Real DB 가 다른 schema 면 silent break.
|
||||
|
||||
// ✅ Integration test (real DB).
|
||||
```
|
||||
|
||||
→ Trophy 가 integration heavy 의 이유.
|
||||
|
||||
### Test Doubles
|
||||
```
|
||||
Stub: 가짜 (return value).
|
||||
Mock: 호출 검증.
|
||||
Spy: 옛 호출 record.
|
||||
Fake: 작은 진짜 구현 (in-memory DB).
|
||||
|
||||
→ Fake > Mock 가 일반.
|
||||
```
|
||||
|
||||
### Boundary test
|
||||
```
|
||||
- Validation: invalid input.
|
||||
- Auth: unauthorized.
|
||||
- Rate limit.
|
||||
- Concurrency.
|
||||
- Failure (network, DB).
|
||||
|
||||
→ Happy path 만 = 부족.
|
||||
```
|
||||
|
||||
### Coverage
|
||||
```
|
||||
80% line coverage = baseline.
|
||||
100% = 가능 가 brittle.
|
||||
60% + critical 100% = 합리.
|
||||
```
|
||||
|
||||
→ Mutation testing 가 진짜 quality.
|
||||
|
||||
→ [[Testing_Stryker_Mutation]].
|
||||
|
||||
### Snapshot test
|
||||
```ts
|
||||
expect(component).toMatchSnapshot();
|
||||
```
|
||||
|
||||
→ Visual regression.
|
||||
주의: 매 변경 = 다른 snapshot. Update 자주.
|
||||
|
||||
### Contract test
|
||||
```ts
|
||||
// Pact / Spectral
|
||||
// API consumer + provider 의 schema.
|
||||
```
|
||||
|
||||
→ Microservice 사이.
|
||||
|
||||
→ [[Testing_Pact_Contract_Deep]].
|
||||
|
||||
### Property test
|
||||
```ts
|
||||
import fc from 'fast-check';
|
||||
|
||||
test('add commutative', () => {
|
||||
fc.assert(fc.property(fc.integer(), fc.integer(), (a, b) => {
|
||||
expect(add(a, b)).toBe(add(b, a));
|
||||
}));
|
||||
});
|
||||
```
|
||||
|
||||
→ 100+ random input. Edge case 가 더.
|
||||
|
||||
### Visual regression
|
||||
```ts
|
||||
// Chromatic / Percy
|
||||
// 매 component 의 screenshot 비교.
|
||||
```
|
||||
|
||||
→ UI 의 unintended change.
|
||||
|
||||
### Test Data
|
||||
```
|
||||
Factory + faker.
|
||||
Per-test reset.
|
||||
Anonymized prod data.
|
||||
```
|
||||
|
||||
→ [[Testing_Test_Data_Management]].
|
||||
|
||||
### CI 의 test
|
||||
```yaml
|
||||
- run: npm run lint
|
||||
- run: npm run typecheck
|
||||
- run: npm test # unit + integration
|
||||
- run: npm run e2e # Playwright
|
||||
```
|
||||
|
||||
→ 매 PR 가 빠른 unit. E2E 가 매일 / 매 deploy.
|
||||
|
||||
### Test 가 안 좋은 패턴
|
||||
```
|
||||
- 모든 거 E2E: 느린, 깨짐.
|
||||
- 모든 거 mock: false confidence.
|
||||
- 매 line 1 test: brittle.
|
||||
- 변경 마다 snapshot update: 의미 X.
|
||||
- "Coverage 100%" 강제: 가짜 test 추가.
|
||||
```
|
||||
|
||||
### Test pyramid 의 이유
|
||||
```
|
||||
- Unit 가 가장 빠름 + 정확.
|
||||
- E2E 가 가장 느린 + 깨짐.
|
||||
|
||||
→ 대부분 = unit. 작은 = E2E.
|
||||
```
|
||||
|
||||
### Trophy 의 이유 (Kent)
|
||||
```
|
||||
- Mock 가 confidence ↓.
|
||||
- Integration 가 진짜 동작.
|
||||
|
||||
→ Integration heavy.
|
||||
```
|
||||
|
||||
### React Testing Library
|
||||
```ts
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
test('button click', () => {
|
||||
render(<Counter />);
|
||||
const btn = screen.getByRole('button');
|
||||
fireEvent.click(btn);
|
||||
expect(screen.getByText('1')).toBeInTheDocument();
|
||||
});
|
||||
```
|
||||
|
||||
→ User-centric (a11y query).
|
||||
|
||||
### Test code 의 quality
|
||||
```
|
||||
- DRY (helper, factory).
|
||||
- 명확 이름 ("user can login").
|
||||
- Setup / teardown 격리.
|
||||
- Fast (slow = skip in CI).
|
||||
- Stable (flaky 0%).
|
||||
```
|
||||
|
||||
### Flaky test
|
||||
```
|
||||
간헐 fail = trust ↓.
|
||||
- Race condition.
|
||||
- Network.
|
||||
- Time.
|
||||
|
||||
→ Find + fix. Don't `it.skip` permanently.
|
||||
```
|
||||
|
||||
### Test 의 ROI
|
||||
```
|
||||
high: critical path E2E, complex logic unit.
|
||||
low: trivial getter, mock heavy.
|
||||
|
||||
→ "이 test 가 fail 시 무엇 catch?"
|
||||
```
|
||||
|
||||
### A/B feature test
|
||||
```
|
||||
새 feature 가 release 가 있어:
|
||||
- Unit: feature flag 의 양쪽.
|
||||
- Integration: 양쪽 path.
|
||||
- E2E: 1 path 만 (cost).
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 상황 | 추천 |
|
||||
|---|---|
|
||||
| 일반 React | Trophy |
|
||||
| Backend API | Pyramid 변형 (integration heavy) |
|
||||
| Library | Pyramid + property |
|
||||
| Critical path | E2E |
|
||||
| 단순 logic | Unit + property |
|
||||
| API contract | Pact |
|
||||
| UI regression | Chromatic / Percy |
|
||||
| Mutation | Stryker |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Ice cream cone**: 느린, brittle.
|
||||
- **Mock 모든 것**: false confidence.
|
||||
- **No static check**: 가장 cheap 만.
|
||||
- **Coverage 100% 강제**: 가짜 test.
|
||||
- **Flaky 무시**: trust ↓.
|
||||
- **E2E 만**: 매 commit slow.
|
||||
- **Test 만 + no E2E**: production 가 처음 진실.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- Trophy 가 modern (integration heavy).
|
||||
- Static (lint + type) = 가장 cheap 의 catch.
|
||||
- Mutation 가 진짜 quality.
|
||||
- Critical path E2E 만.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Testing_Test_Pyramid]]
|
||||
- [[Testing_Stryker_Mutation]]
|
||||
- [[Testing_Property_Based]]
|
||||
Reference in New Issue
Block a user