화이트박스 테스트 설계와 커버리지 기반 품질 게이트
화이트박스 테스트의 커버리지 기준과 CFG 기반 설계, CI 품질 게이트 적용 방식 및 코드 예시를 정리한다.
2026-08-14 · 최초 발행 2025-12-22
코드 구조를 테스트 범위로 바꾸는 방법
구조 기반 테스트는 소스 코드의 제어 흐름, 조건 분기, 루프, 데이터 정의·사용 지점을 따라 테스트 케이스를 구성한다. 실행 여부만 확인하는 데 그치지 않고, 어떤 문장·분기·조건·경로가 실제로 검증됐는지를 커버리지로 측정한다.
이 과정에서 만드는 결과물은 제어 흐름 그래프(CFG), 입력·기대값·경로를 담은 테스트 케이스, 커버리지 리포트, 결함 목록이다. 일반적인 흐름은 정적 분석, 커버리지 기준 선택, 경로 선정과 테스트 설계, 계측·실행, 리포팅과 게이팅으로 이어진다.
커버리지 기준은 리스크에 맞춰 선택한다
문장, 분기, 조건, 경로, MC/DC, 데이터 흐름(Definition-Use)은 모두 코드 구조를 보는 기준이지만, 필요한 테스트 수와 검출할 수 있는 결함의 범위가 다르다.
문장·분기·MC/DC·데이터 흐름 가운데 무엇을 우선할지 정하고 목표치를 설정한다. 예를 들어 라인 80%, 브랜치 70%를 기준으로 둘 수 있다. JaCoCo, coverage.py 같은 계측 도구와 연계하고 XML·HTML 리포트 형식을 표준화하면 팀 단위의 추적이 쉬워진다.
테스트 케이스는 CFG를 바탕으로 분석하되, 모든 경로를 동등하게 다루기보다 리스크가 큰 경로를 먼저 고른다. 비즈니스 규칙과 예외 조건을 테스트 오라클로 정의하고, 경계값과 오류 경로도 함께 포함한다.
순환 복잡도(Cyclomatic Complexity)와 결함 이력은 집중 테스트 대상을 고르는 근거가 된다. 안전·금융 모듈에는 MC/DC처럼 강한 기준을 적용하고, 유틸리티 코드는 비용 대비 효과를 고려해 기준을 완화할 수 있다. Mock·Stub으로 모듈을 격리하고, I/O·동시성 코드에는 타이밍 제어를 마련해야 관찰 가능성을 확보할 수 있다. 이때 계측 오버헤드와 테스트 시간의 균형도 관리 대상이다.
기준별 검증 범위와 운영 부담
| 기준 | 결함 검출력(효과) | 실행 비용(성능) | 확장성 | 일관성 | 운영 편의 |
|---|---|---|---|---|---|
| 문장(Statement) | 낮음~보통, 누락 코드 검출 적합 | 낮음, 빠른 실행 | 높음, 대규모에도 용이 | 높음 | 매우 높음 |
| 분기(Branch) | 보통~높음, 의사결정 결함에 강함 | 낮음~보통 | 높음 | 높음 | 높음 |
| 조건/MC/DC | 높음, 복합 조건 오류에 강함 | 보통~높음, 케이스 증가 | 중간, 복잡도 영향 | 높음 | 중간 |
| 경로(Path) | 매우 높음(이론적), 실용성 제약 | 매우 높음 | 낮음, 조합 폭발 | 중간 | 낮음 |
| 데이터 흐름(Def-Use) | 보통~높음, 상태/변수 사용 결함 | 보통 | 중간 | 중간 | 중간 |
문장 커버리지는 빠르게 누락 코드를 찾기 좋고 대규모 코드베이스에도 적용하기 쉽다. 분기 커버리지는 의사결정 결함을 더 잘 드러낸다. 복합 조건이 중요한 경우 조건 커버리지와 MC/DC가 유리하지만 케이스 수가 늘어난다. 경로 커버리지는 이론적인 검출력이 높아도 조합 폭발 때문에 적용에 제약이 있다. 데이터 흐름 테스트는 변수의 정의와 사용 관계에서 발생하는 상태·사용 결함을 겨냥한다.
커버리지 리포트가 병합 판단으로 이어지는 흐름
CI 파이프라인에서는 커버리지 기준을 품질 게이트로 두고, 미달 시 병합을 막을 수 있다. 변경 감지 기반의 증분 테스트를 함께 사용하면 실행 시간을 줄이는 데 도움이 된다.
안전·미션 크리티컬 시스템에서는 항공·의료·자동차 제어 로직에 MC/DC를 적용해 인증 근거 자료로 활용할 수 있다. 레거시 리팩터링에서는 브랜치 커버리지 80% 이상을 목표로 기존 기능 변경 위험을 관리한다. 마이크로서비스와 API 게이트웨이에서는 특성 플래그와 롤백 경로를 포함해 분기를 검증하고, 배포 전에 게이트를 통과시키는 방식이 가능하다. 데이터 파이프라인은 Def-Use 기반 테스트로 변환·필터링 경로를 확인하며, 보안 입력 검증에서는 화이트리스트·블랙리스트 분기와 예외 처리 경로를 포괄한다.
Python에서 분기와 예외 경로를 검증하기
환경
- 언어/도구: Python 3.11, pytest 8.x, coverage.py 7.x
- 전제: 가상환경 사용, POSIX 셸 기준
예제 코드
# src/fees.py
def calc_fee(amount: float, is_vip: bool) -> float:
if amount < 0:
raise ValueError("negative amount")
discount = 0.1 if is_vip else 0.0
fee = amount * (1 - discount)
if fee > 1000:
fee *= 0.95 # high-amount extra discount
return round(fee, 2)
아래 테스트는 음수 입력 예외, VIP 여부, fee > 1000 분기를 각각 통과하도록 구성한다.
# tests/test_fees.py
import pytest
from src.fees import calc_fee
def test_negative_amount():
with pytest.raises(ValueError):
calc_fee(-1, False)
def test_non_vip_under_threshold():
assert calc_fee(100, False) == 100.0
def test_vip_under_threshold():
assert calc_fee(100, True) == 90.0
def test_vip_over_threshold_triggers_extra():
# ensures branch 'fee > 1000'
assert calc_fee(2000, True) == round(2000 * 0.9 * 0.95, 2)
실행과 커버리지 게이트
pip install pytest coverage
coverage run -m pytest -q
coverage report -m
coverage html # htmlcov/index.html에서 상세 경로 확인
다음 게이트는 라인 커버리지가 80% 미만이면 실패한다.
# 간이 게이트: 라인 커버리지 80% 미만이면 실패
coverage xml
python - << 'PY'
import sys, xml.etree.ElementTree as ET
rate = float(ET.parse('coverage.xml').getroot().get('line-rate'))
threshold = 0.80
print(f"line-rate={rate:.3f}, threshold={threshold:.2f}")
sys.exit(0 if rate >= threshold else 1)
PY
변이 테스트는 mutmut, PIT로 테스트 강도를 평가하고 살해율 60~80%를 목표로 적용할 수 있다. 복합 조건은 De Morgan과 경계값을 사용해 분해하고 MC/DC 충족 경로를 설계한다. 데이터 흐름 테스트는 변수 정의-사용 쌍(Def-Use)을 추출하는 도구나 정적 분석 결과를 기반으로 케이스를 보강한다.
품질 지표를 배포 안전망으로 활용할 때
브랜치 커버리지를 40%→80%로 높이면 릴리즈 후 결함 밀도가 2050% 감소할 것으로 기대할 수 있으며, 이는 도메인·품질 성숙도에 따라 달라진다. 변경 영향 경로를 커버하면 긴급 롤백 비율은 1530% 감소할 수 있다.
팀과 모듈이 같은 커버리지 메트릭을 사용하면 품질 상태를 즉시 파악할 수 있다. 커버리지 게이트는 기준 미달 코드의 병합을 차단해 릴리즈 실패율을 1025% 낮추는 효과를 기대할 수 있다. 다만 계측과 실행으로 520%의 오버헤드가 늘 수 있으므로, 증분 실행과 테스트 병렬화로 상쇄하는 전략이 필요하다.
구조 기반 테스트의 핵심은 높은 숫자를 목표로 삼는 데 있지 않다. 코드 구조와 리스크에 맞는 기준을 고르고, 테스트 결과를 CI의 병합·배포 판단에 연결하는 데 있다.