테스트 층과 도구 선택 — Vitest·pytest·Cypress·Playwright
단위·통합·브라우저·운영 smoke 검증을 구분하고, Vitest·pytest·Cypress·Playwright를 겹치지 않게 배치하는 기준을 정리합니다.
목차
테스트 도구는 이름보다 어떤 경계를 실제로 통과하는가로 구분해야 합니다. 브라우저를 띄웠다고 항상 E2E는 아니며, mock이 많다고 무조건 단위 테스트도 아닙니다. 먼저 실패했을 때 알고 싶은 사실을 정하고 가장 좁은 층을 고릅니다.
검증 범위를 넓히는 순서
입력과 출력만 빠르게 확인합니다.
프레임워크 경계와 협력 객체를 확인합니다.
실제 DB·캐시·메시지 브로커와 계약을 확인합니다.
사용자의 핵심 행동과 화면 상태를 확인합니다.
배포된 읽기 경로와 readiness만 확인합니다.
도구는 책임으로 고릅니다
| 도구 | 강한 영역 | 대표 검증 | 피해야 할 오용 |
|---|---|---|---|
| Vitest | TypeScript·React의 빠른 단위/컴포넌트 테스트 | 순수 함수, hook, 렌더 상태, mock 경계 | 실제 브라우저 동작을 jsdom만으로 확정 |
| pytest | Python 단위·API·스케줄러 테스트 | 파서, FastAPI 계약, 시간대, 재시도, DB adapter | mock 호출 횟수만으로 DB 제약을 증명 |
| Cypress | 브라우저 안에서 실행되는 사용자 흐름과 네트워크 제어 | 폼, 라우팅, 실패 응답, 재시도 UI | 모든 내부 구현을 브라우저에서 반복 검사 |
| Playwright | 여러 브라우저·컨텍스트·탭과 API를 아우르는 E2E | 인증, 팝업, 다중 사용자, 라우트 smoke | 전체 페이지에서 상태 코드만 보고 업무 성공으로 간주 |
Cypress와 Playwright는 경쟁 목록이 아니라 필요한 상호작용에 따라 선택하는 브라우저 도구입니다. 한 제품에서 둘을 쓴다면 “Cypress는 화면 중심 회귀, Playwright는 다중 컨텍스트와 배포 smoke”처럼 소유 범위를 문서와 스크립트로 고정합니다.
같은 결함을 여러 층에서 반복하지 않습니다
로그인 버튼의 disabled 조건은 컴포넌트 테스트가 빠릅니다. 실제 쿠키가 다음 요청에 전달되는지는 브라우저 E2E가 맞습니다. 세션 저장소의 만료 제약은 통합 테스트가 맡습니다.
중복 테스트와 보완 테스트
같은 조건문을 Vitest와 Cypress에서 똑같이 재현합니다.
Vitest는 상태 전이를, Cypress는 사용자 행동과 네트워크 실패를 확인합니다.
배포 후에는 쓰기 시나리오 대신 health·readiness·공개 읽기만 확인합니다.
테스트가 실패했을 때 원인을 한 층으로 좁힐 수 있어야 합니다. 모든 테스트가 실제 DB와 브라우저를 함께 띄우면 느릴 뿐 아니라 어떤 계약이 깨졌는지도 흐려집니다.
mock과 실제 의존성의 경계
mock은 내 코드가 의존성을 어떻게 호출하는지 확인할 때 유용합니다. DB의 UNIQUE 제약, 트랜잭션 격리, SQL 문법, Redis TTL처럼 외부 시스템이 보장하는 성질은 실제 인스턴스가 필요합니다.
mock으로 충분
파서 입력 → 출력
외부 실패 → 오류 상태 변환
재시도 횟수와 backoff 상한
실제 의존성이 필요
동시 INSERT의 중복 방지
transaction rollback
index를 사용한 query plan
cache expiry와 원본 재조회
Testcontainers나 격리된 Compose를 쓰더라도 운영 DB를 테스트 대상으로 삼지 않습니다. 데이터 초기화와 쓰기 검증은 DEV·임시 인스턴스에서 끝내고, 운영에서는 비파괴 smoke만 수행합니다.
브라우저 테스트의 비용을 제한합니다
브라우저 테스트는 실행 시간뿐 아니라 이미지 다운로드·CI 트래픽·실패 분석 비용이 큽니다. 다음 순서로 범위를 줄입니다.
- 매 PR에서는 핵심 사용자 흐름과 변경된 영역만 실행합니다.
- 전체 브라우저 조합은 예약 실행이나 릴리스 게이트로 분리합니다.
- production 이미지 빌드에서는 E2E 브라우저 바이너리를 설치하지 않습니다.
- 실패 시 screenshot·trace·network log를 남기되 개인정보와 token은 제거합니다.
- 운영 smoke는 GET·HEAD 중심으로 구성하고 mutation을 기본 차단합니다.
실패 상태도 계약입니다
성공 경로만 통과하면 사용자는 장애 때 빈 화면을 봅니다. 최소한 다음 상태를 검증합니다.
- 로딩 중 중복 제출이 차단되는가
- 4xx가 입력 수정 안내로 보이는가
- 5xx와 네트워크 단절에 재시도 수단이 있는가
- 빈 결과와 조회 실패가 서로 다른가
- 부분 성공을 전체 성공으로 표시하지 않는가
- retry가 같은 쓰기를 중복 생성하지 않는가
완료 기준
테스트 개수는 품질 지표가 아닙니다. 변경한 계약과 위험이 어떤 증거로 보호되는지 설명할 수 있어야 합니다.
- 가장 좁은 테스트가 결함을 재현하고 수정 후 통과합니다.
- 실제 의존성의 보장은 통합 테스트에서 확인합니다.
- 핵심 사용자 길은 브라우저에서 확인합니다.
- build·lint·type check가 통과합니다.
- 배포 후 health·readiness·공개 smoke가 통과합니다.
- 운영 쓰기와 유료 외부 호출은 기본적으로 실행하지 않습니다.
Next
- testcontainers
- vitest-philosophy
- e2e-equivalence-manifest
Vitest · pytest · Cypress · Playwright 공식 문서를 함께 참고합니다.