236 lines
8.6 KiB
Markdown
236 lines
8.6 KiB
Markdown
# 테스트 원칙
|
|
|
|
**작성일**: 2025-01-03
|
|
**수정일**: 2026-03-16 (robeing/tests 통합 관리 원칙 반영)
|
|
**상위 원칙**: [0_VALUE Test Principles](../../../../0_VALUE/02_Governance/test-principles.md)
|
|
**참고**: 311_backend_coding_principles.md, 312_writing-principles.md, ../../../../0_VALUE/02_Governance/coding-principles.md
|
|
|
|
---
|
|
|
|
## 1. 핵심 원칙
|
|
|
|
### TDD 원칙
|
|
|
|
**테스트는 TDD로 진행한다.**
|
|
|
|
- **Red**: 테스트 먼저 작성 (기대 행동을 테스트에 명시)
|
|
- **Green**: 최소 구현으로 테스트 통과
|
|
- **Refactor**: 리팩터링 후 테스트 재실행
|
|
|
|
DecisionEngine, intent, 캘린더 로직 등은 항상 테스트에서 기대 행동을 확정한 뒤 수정한다.
|
|
|
|
### 로빙 테스트 환경 단일화 원칙
|
|
|
|
로빙 테스트는 `robeing/tests`에서 통합 관리한다. rb8001, skill-* 등 서비스별 테스트를 `robeing/tests/rb8001/` 등 하위에 두고, 단일 실행 기준만 사용한다. rb8001 푸시 시 Gitea Actions 배포가 트리거되므로, 테스트 수정만으로 배포를 유발하지 않도록 robeing/tests에서 작업한다. 임시/분기 테스트 환경은 만들지 않는다.
|
|
|
|
### UX 검증 원칙
|
|
|
|
- 사용자가 실제로 보는 문구·행동만 검증한다 (예: 응답 문장, 버튼 노출 여부)
|
|
- 내부 안내/제약 설명(“직접 언급 금지” 같은 시스템 지시문)은 금지어 검증 대상이 아님
|
|
- UX 규칙(감정 직접 표현 금지 등)은 사용자 시나리오 테스트로 재현한다
|
|
|
|
### 테스트 코드 재작성 비효율 방지
|
|
|
|
**목표**: 테스트 코드를 매번 재작성하지 않고 재사용 가능하게 관리
|
|
|
|
**원칙** (상세: 섹션 2-5 참조):
|
|
- **재사용성 우선**: 공통 테스트 로직은 fixtures/유틸리티로 추출 (섹션 3)
|
|
- **자동화 필수**: pytest 기반 자동 테스트만 유지 (섹션 6)
|
|
- **중복 제거**: 동일 기능 테스트는 하나로 통합 (섹션 4)
|
|
|
|
---
|
|
|
|
## 2. 테스트 파일 분류 및 위치
|
|
|
|
| 유형 | 위치 | 관리 방식 |
|
|
|------|------|----------|
|
|
| **로빙 통합 테스트** | `robeing/tests/` (rb8001/, test_*.py 등) | robeing 전체 테스트 통합. 배포 트리거 없이 실행. [worklog 260316](../../journey/worklog/260316_rb8001_테스트_로빙테스츠로_이전.md) |
|
|
| **pytest 자동 테스트** | `tests/test_*.py` (레포 내) | 버전 관리, 지속 유지 (.gitignore 제외 금지) |
|
|
| **실험/관찰 스크립트** | `scripts/` (임시) | 작업 완료 즉시 삭제 |
|
|
| **공통 fixtures** | `tests/conftest.py` | 재사용 우선 |
|
|
| **테스트 데이터** | `tests/data/`, `tests/fixtures/` | 공유 가능하도록 구조화 |
|
|
|
|
### 금지 사항
|
|
|
|
| 금지 | 이유 | 참조 |
|
|
|------|------|------|
|
|
| `tests/`에 실험 스크립트 저장 | 자동 테스트와 혼재, 정리 어려움 | 섹션 5, 6 |
|
|
| 수동 실행 스크립트 장기 보관 | 재작성 비효율 유발 | 섹션 5 |
|
|
| 중복 테스트 파일 유지 | 유지보수 비용 증가 | 섹션 4 |
|
|
| `.gitignore`에 `tests/test_*.py` 제외 | 버전 관리 필요 | 섹션 2 |
|
|
|
|
---
|
|
|
|
## 3. 테스트 코드 재사용성
|
|
|
|
### 공통 로직 추출
|
|
|
|
**권장**: pytest fixtures 사용
|
|
```python
|
|
# tests/conftest.py
|
|
@pytest.fixture
|
|
def mock_llm_service():
|
|
"""LLM 서비스 모킹 - 모든 테스트에서 재사용"""
|
|
with patch('app.services.llm.llm_service.LLMService') as mock:
|
|
yield mock
|
|
|
|
# tests/test_intent.py
|
|
def test_intent_classification(mock_llm_service):
|
|
# mock_llm_service 자동 주입
|
|
...
|
|
```
|
|
|
|
**금지**: 각 테스트 파일에서 동일한 모킹 코드 반복 작성
|
|
|
|
### 테스트 데이터 관리
|
|
|
|
**권장**: 공통 데이터 파일 사용
|
|
- `tests/data/intent_eval_samples.json`: 의도 분류 샘플 데이터
|
|
- `tests/fixtures/`: 재사용 가능한 모킹 객체
|
|
|
|
**금지**: 테스트 파일 내 하드코딩된 데이터 반복
|
|
|
|
---
|
|
|
|
## 4. 중복 테스트 통합
|
|
|
|
### 통합 대상
|
|
|
|
| 중복 패턴 | 처리 방법 |
|
|
|-----------|----------|
|
|
| `test_overlap_cases.py` + `test_overlap_tdd.py` | 하나로 통합 (최종 버전 유지) |
|
|
| TDD 단계별 테스트 파일 | 최종 버전만 유지 |
|
|
| `test_*_vs_*.py` 비교 테스트 | troubleshooting 기록 후 삭제 |
|
|
|
|
### 유지 기준 (상세: 섹션 6 참조)
|
|
|
|
- **유지**: pytest로 재현 가능한 자동 테스트
|
|
- **삭제**: 임시 실험, 비교 테스트, refactoring 테스트 (섹션 5)
|
|
|
|
---
|
|
|
|
## 5. 임시 테스트 정리
|
|
|
|
### 즉시 삭제 대상
|
|
|
|
| 대상 | 패턴 | 처리 시점 |
|
|
|------|------|----------|
|
|
| **리팩토링 테스트** | `test_*_refactoring.py` | 작업 완료 후 |
|
|
| **비교 실험** | `test_*_vs_*.py` | troubleshooting 기록 후 |
|
|
| **관찰용 스크립트** | "서비스 코드 수정 없이 관찰" 주석 | 작업 완료 즉시 |
|
|
| **수동 실행 스크립트** | `python3` 직접 실행, pytest 미적용 | 작업 완료 즉시 (섹션 6) |
|
|
| **실험 결과 파일** | `.md`, `.json` 결과 파일 | troubleshooting 문서화 후 |
|
|
|
|
### 정리 프로세스
|
|
|
|
1. 문제 해결 → troubleshooting 문서 작성
|
|
2. 핵심 교훈 기록
|
|
3. 임시 테스트 파일 삭제
|
|
|
|
---
|
|
|
|
## 6. pytest 자동 테스트 기준
|
|
|
|
### 필수 요구사항
|
|
|
|
**pytest 자동 테스트 확인 방법**:
|
|
- `import pytest` 또는 `@pytest.mark.*` 데코레이터 사용
|
|
- `pytest tests/test_*.py` 명령으로 실행 가능
|
|
- `if __name__ == "__main__"` 직접 실행 코드 없어야 함
|
|
|
|
**금지 사항**:
|
|
- `python3 tests/test_*.py` 방식의 수동 실행 스크립트
|
|
- "pytest 없이 직접 실행" 주석이 있는 파일
|
|
- pytest import 없이 `def test_*()` 함수만 있는 파일 (pytest 미적용)
|
|
|
|
### 테스트 파일 검증
|
|
|
|
**추가 전 확인**:
|
|
```bash
|
|
# pytest로 실행 가능한지 확인
|
|
pytest tests/test_*.py -v
|
|
|
|
# pytest import 여부 확인
|
|
grep -l "import pytest\|@pytest" tests/test_*.py
|
|
```
|
|
|
|
### 컨테이너 테스트 파일 반영
|
|
|
|
**배경**: tests 디렉토리는 볼륨 마운트가 아님 (Dockerfile에서 `COPY ./tests /code/tests`로 빌드 시만 복사)
|
|
|
|
**새 테스트 파일 추가 시**:
|
|
```bash
|
|
# 방법 1: docker cp (빠름, 재빌드 불필요)
|
|
docker cp tests/test_new_feature.py rb8001:/code/tests/
|
|
|
|
# 방법 2: 컨테이너 재빌드 (확실함)
|
|
docker compose down && docker compose up -d --build
|
|
```
|
|
|
|
**주의**: 호스트에서 테스트 파일 수정 후 컨테이너에 반영 안 되는 문제 주의
|
|
|
|
---
|
|
|
|
## 7. 테스트 네이밍 및 구조
|
|
|
|
### 파일명 규칙
|
|
|
|
| 패턴 | 용도 | 예시 |
|
|
|------|------|------|
|
|
| `test_{기능}.py` | 기능 단위 테스트 | `test_intent_classification.py` |
|
|
| `test_{엔드포인트}_endpoint.py` | API 엔드포인트 테스트 | `test_diary_endpoint.py` |
|
|
| `e2e/test_{워크플로우}.py` | E2E 테스트 | `e2e/test_coldmail_full_flow.py` |
|
|
|
|
### 금지 네이밍
|
|
|
|
- `test_*_refactoring.py` (작업 완료 후 삭제)
|
|
- `test_*_vs_*.py` (비교 실험, 결과 기록 후 삭제)
|
|
- `e2e_*.py` (tests 루트에, e2e/ 폴더로 이동)
|
|
- 중복된 e2e 테스트: `test_e2e_*.py`와 `test_*_e2e.py` (하나로 통합)
|
|
|
|
---
|
|
|
|
## 8. 체크리스트
|
|
|
|
### 테스트 작성 전
|
|
|
|
- [ ] TDD 순서 준수 (Red → Green → Refactor)
|
|
- [ ] 동일한 테스트 로직이 이미 존재하는가? (섹션 3 재사용성 확인)
|
|
- [ ] 공통 모킹/데이터가 있다면 `conftest.py`에 추가할 수 있는가? (섹션 3)
|
|
- [ ] pytest 자동 테스트인가? (섹션 6 기준 확인)
|
|
|
|
### 테스트 파일 추가 전
|
|
|
|
- [ ] 중복 테스트 파일이 있는가? (섹션 4 통합 가능한지 확인)
|
|
- [ ] 임시 실험 파일인가? (섹션 5 삭제 계획 수립)
|
|
- [ ] 네이밍 규칙 준수 (섹션 7)
|
|
- [ ] `.gitignore`에 제외 규칙이 있는가? (섹션 2 - 테스트 파일은 버전 관리 필수)
|
|
|
|
### 정리 시점 (섹션 5, 9 참조)
|
|
|
|
- [ ] 리팩토링/비교 테스트 → troubleshooting 기록 후 삭제
|
|
- [ ] 실험/수동 스크립트 → 작업 완료 즉시 삭제
|
|
- [ ] 중복 테스트 → 통합 또는 삭제
|
|
|
|
---
|
|
|
|
## 9. 정리 프로세스 예시
|
|
|
|
### 실제 정리 사례 (2025-01-03)
|
|
|
|
**삭제된 파일**:
|
|
1. `test_calendar_intent_overlap_cases.py` - "서비스 코드 수정 없이 관찰" 목적, 수동 실행
|
|
2. `test_calendar_query_vs_create.py` - `python3` 직접 실행, pytest 미적용
|
|
3. `test_all_day_event_simple.py` - 수동 실행 스크립트
|
|
4. `e2e/test_e2e_message_flow.py` - "pytest 없이 직접 실행" 주석, 중복 (`test_message_flow_e2e.py` 존재)
|
|
|
|
**결과**: 68개 → 64개 (pytest 자동 테스트만 유지)
|
|
|
|
---
|
|
|
|
## 10. 참고
|
|
|
|
- FastAPI 구조 원칙: `311_backend_coding_principles.md` 섹션 18 (테스트 원칙)
|
|
- 문서 작성 원칙: `312_writing-principles.md` (troubleshooting 기록 방법)
|
|
- 트러블슈팅: `journey/troubleshooting/251110_레거시_테스트_폴더_정리.md`
|