1단계
테스트 피라미드 · 트레이드오프
20 분
테스트 피라미드 · 트레이드오프
"테스트 많이 쓰세요" 는 도움이 안 됩니다. 어디에 얼마나 쓸지가 진짜 설계.
1. 피라미드
/ E2E \ 5%
/--------\
/ 통합 \ 15%
/------------\
/ 단위 \ 80%
/----------------\
밑이 넓고 위가 좁음. 단위 테스트가 대부분, E2E 는 최소.
2. 각 계층의 특성
| 계층 | 예 | 속도 | 신뢰 | 취약 |
|---|---|---|---|---|
| 단위 | cn() · formatDate · 순수함수 |
<1ms | 낮음 | 낮음 |
| 통합 | DB + 쿼리 · API route | 100ms~1s | 높음 | 중간 |
| E2E | 브라우저 자동화 | 1~30s | 매우 높음 | 높음 |
E2E 는 flaky 하기 쉬움. 네트워크 · 타이밍 이슈로 회귀 알림을 울리는데 실제 버그가 아닌 경우 다수.
3. 언제 어느 계층?
- 입력 → 출력이 순수 → 단위 (vitest · pytest)
- DB · 외부 API 포함 → 통합 (testcontainers)
- 사용자 플로우 (로그인 → 구매) → E2E (Playwright)
순수 함수까지 E2E 로 감싸면 실행 시간이 쓸데없이 길어짐.
4. 커버리지 % 는 목표가 아님
80% coverage 이 신뢰 80% 를 뜻하지 않습니다. if (x) {} else {} 양쪽 실행이 버그 없음을 보장하지 않음. 중요한 건 시나리오 커버리지.
- 로그인 성공
- 로그인 실패 (비번 틀림)
- 세션 만료
- rate limit 초과
이 4 개가 검증되면 login.ts 는 충분. 라인 수 %가 아님.
5. "테스트가 느려서 안 돌려" 의 해결
- 단위는 watch 모드 (
vitest --watch) 로 저장 즉시 - 통합은 CI 에서만
- E2E 는 PR 머지 전 1 회
로컬에서 E2E 까지 매번 돌리면 생산성 급락. 계층별 실행 타이밍 분리.
6. flaky 테스트 처리
- 1 번 실패 → warn, 3 번 실패 → block
- 재현 불가하면
test.skip()+ TODO. 건드리면 안 되는 자물쇠가 아니라 "일단 보류, 2 주 내 해결" - 근본 원인 (타이밍 · 네트워크 · 상태 공유) 를 찾아 제거
7. 테스트는 문서의 일부
describe("logAdminAction", () => {
it("reason 30자 미만이면 reject", ...);
it("fire-and-forget — 실패해도 메인 요청은 200", ...);
it("request 없으면 cookies() fallback 으로 user_id 채움", ...);
});
describe / it 이름이 명세. 6 개월 뒤 내 코드를 다시 볼 때 가장 빠른 이해 경로.
하고픈 말
테스트 피라미드는 규칙이 아니라 균형입니다. 스타트업 MVP 는 E2E 1 ~ 2 개 + 단위 몇 개도 괜찮고, 결제 · 의료 같은 고위험은 E2E 비중을 키웁니다. 프로젝트 사고 비용에 맞춰 분배.
Next
- 02-vitest-mock-patterns