Files

5.3 KiB

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의 "측정 조건 (재현용)" 섹션 참고.