# 2026-05-04 jqwik 속성 기반 테스트 도입 ## 한 줄 결론 보안 핵심(LogSanitizer)에 속성 기반 테스트를 추가하여 **application 모듈 분기 커버리지를 14.5% → 78.9%로 +64.4%p 끌어올렸으며**, 매퍼/JSON Pointer 변환에 대한 회귀 안전망(presentation 라인 3.4% → 24.8%)을 동시에 확보했다. 같은 측정 조건(exclusion 없음, 같은 명령어, 같은 도구 버전)에서 사과-사과 비교한 결과다. ## 변경 범위 - 신규 jqwik 의존성 도입 (1.9.3) — JUnit Platform에 `includeEngines` 추가 - 신규 속성 테스트 4개 클래스, 총 30개 속성 - `LogSanitizerPropertyTest` (11 속성, application 모듈) - `ApiErrorHttpStatusMapperPropertyTest` (5 속성, presentation 모듈) - `JsonPointerConversionPropertyTest` (8 속성, presentation 모듈) - `RequestBoundApiResultFactoryPropertyTest` (6 속성, bootstrap 모듈) - JaCoCo + PIT 플러그인 도입, Tier 기반 임계치 정책 수립 - 영향 범위: application / presentation / bootstrap 모듈 ## Before / After 수치 (사과-사과 측정) ### 라인 커버리지 | 모듈 | BEFORE | AFTER | 변화 | |---|---|---|---| | `application` | 42.3% | **70.9%** | **+28.6%p** | | `presentation` | 3.4% | **24.8%** | **+21.4%p** | | `infrastructure` | 81.5% | 81.5% | — | | `bootstrap` | 80.7% | 80.3% | -0.4%p (오차) | ### 분기 커버리지 | 모듈 | BEFORE | AFTER | 변화 | |---|---|---|---| | `application` | 14.5% | **78.9%** | **+64.4%p** | | `presentation` | 3.3% | **42.3%** | **+39.0%p** | | `infrastructure` | (브랜치 없음) | (브랜치 없음) | — | | `bootstrap` | 59.7% | 56.6% | -3.1%p | ### 메서드 / 클래스 | 모듈 | METHOD before → after | CLASS before → after | |---|---|---| | `application` | 41.8% → 63.6% | 75.0% → 81.2% | | `presentation` | 5.9% → 25.0% | 18.2% → 40.0% | | `infrastructure` | 79.2% → 79.2% | 100% → 100% | | `bootstrap` | 82.9% → 83.8% | 85.7% → 85.2% | ### PIT (단언 강도 측정) | 모듈 | Mutations | Killed | Test Strength | |---|---|---|---| | `application` | 64 | **46 (72%)** | 82% | | `presentation` | 75 | 24 (32%) | **92%** (커버된 영역에서) | > **PIT는 BEFORE에 측정되지 않았다.** PIT 플러그인 자체가 이 사건에서 처음 도입되었기 때문에 비교값은 없으며, AFTER의 절대 수치만 의미를 갖는다. ## 자동 검출 가능해진 회귀 시나리오 이전에는 단위 테스트가 0개였던 영역에 임의 입력 fuzz가 추가되어, 다음 회귀가 PR 단계에서 즉시 잡힌다: 1. **`LogSanitizer.clientIp` 마스킹 누락** — 임의의 0~255 옥텟 IPv4 주소 1만건에 대해 마지막 옥텟이 0으로 마스킹되지 않으면 즉시 실패. 2. **매퍼의 `AUTHENTICATION_REQUIRED → 401` 변경** — 정확값 단언 + PIT가 잡음. 3. **신규 `ClientFacingErrorCode` 추가 후 매퍼 분기 누락** — 매핑 테이블 가드 + classpath 가드 + 속성 테스트의 3중 안전망. 4. **JSON Pointer `~`/`/` 이스케이프 누락 (RFC 6901 위반)** — 임의 입력 fuzz로 즉시 실패. 5. **TraceIdFilter 누락 시 traceId가 JSON `null`로 직렬화되는 회귀** — sentinel `"-"` 강제. ## 측정 조건 (재현용) | 항목 | 값 | |---|---| | BEFORE 커밋 | `c9bfdf2` (2026-05-03) | | AFTER 커밋 | `6f62490` (2026-05-04) — measurement run에서 build.gradle의 `**/config/**` exclusion 임시 제거 | | 측정 명령어 | `./gradlew clean test jacocoTestReport` | | PIT 측정 명령어 | `./gradlew :application:pitest :presentation:pitest` | | JaCoCo 버전 | 0.8.13 | | PIT 버전 | 1.19.1 (junit5-plugin 1.2.2) | | jqwik 버전 | 1.9.3 | > **운영 빌드 차이점**: 운영 `build.gradle`에는 `**/config/**` exclusion이 적용되어 있어 일반 `./gradlew jacocoTestReport`는 bootstrap을 0%로 보고한다. 본 비교는 사과-사과를 위해 그 exclusion을 임시 제거한 측정값이다. exclusion 정밀화는 별도 follow-up. ## 한계 / 미해결 1. **presentation의 PIT mutation kill이 32%로 보이는 이유**는 통합 테스트가 bootstrap 모듈에 있어 PIT 측정에서 빠지기 때문이다. *커버된 영역에서의 단언 강도는 92%로 매우 높다*. 다음 사건: 멀티모듈 aggregate 리포트 도입. 2. **bootstrap의 운영 빌드 0%**는 측정 누락이지 코드 미커버가 아니다. 다음 사건: `**/config/**` exclusion을 핸들러를 포함하지 않도록 정밀화. 3. **Tier 1 임계치(95%+)에는 미도달.** 현재는 baseline 모드(게이트 비활성)이며, 점진적 상향 일정은 `docs/testing-coverage-policy.md`에 정의됨. ## 첨부 (원본 리포트) - BEFORE 스냅샷: `coverage-history/v0.0.1-SNAPSHOT/2026-05-04-jqwik-before/` - AFTER 스냅샷: `coverage-history/v0.0.1-SNAPSHOT/2026-05-04-jqwik-after/` 각 스냅샷의 모듈별 진입점: ``` {snapshot}/application/jacoco/html/index.html # JaCoCo 라인/분기 표 {snapshot}/application/pitest/index.html # PIT (AFTER 스냅샷에만 존재) {snapshot}/presentation/jacoco/html/index.html {snapshot}/presentation/pitest/index.html {snapshot}/bootstrap/jacoco/html/index.html {snapshot}/infrastructure/jacoco/html/index.html ``` > `coverage-history/`는 git ignore 되어 로컬 전용. 같은 측정을 재현하려면 > SUMMARY.md의 "측정 조건 (재현용)" 섹션 참고.