용어집 · 테스트 기초
코드 커버리지
영어: Code coverage
다른 이름: 테스트 커버리지, 커버리지, 구문 커버리지, 분기 커버리지
정의
테스트 실행 중에 실행된 소스 코드(라인, 구문, 분기, 함수)의 비율로, 무엇이 잘 테스트되었는지가 아니라 무엇이 테스트되지 않았는지를 보여 줍니다.
코드 커버리지(Code coverage)는 테스트 실행이 코드를 얼마나 실행했는지를 측정합니다. 도구가 코드를 계측(instrument)하고(Java는 JaCoCo, JavaScript는 Istanbul/nyc 또는 V8 coverage, Python은 coverage.py), 테스트를 실행한 뒤 어떤 라인과 분기가 실행되었는지 보고합니다. 화이트박스 지표이며, 주로 단위 테스트와 통합 테스트에서 수집합니다.
커버리지의 종류
다음 코드를 생각해 봅시다.
int shippingCost(double total, boolean isMember) {
if (total >= 100 || isMember) {
return 0;
}
return 5;
}
- 구문(라인) 커버리지: 두
return문이 모두 실행되었는가?(150, false)와(20, false)두 테스트로 100%가 됩니다. - 분기(결정) 커버리지:
if가 참과 거짓 모두 되었는가? 같은 두 테스트로 역시 100%가 됩니다. - 조건 커버리지: 각 불리언 하위 조건이 참과 거짓으로 모두 평가되었는가? 위 테스트는
isMember를 한 번도 참으로 만들지 않았으므로 완전하지 않습니다.(20, true)를 추가하면 해결됩니다. - 함수 커버리지: 각 함수가 적어도 한 번 호출되었는가.
한 줄에 여러 경로가 숨어 있을 수 있으므로, 일반적으로 라인 커버리지보다 분기 커버리지가 더 유용한 목표입니다.
커버리지가 알려 주지 않는 것
커버리지는 코드가 실행되었다는 것을 증명할 뿐, 검증되었다는 것을 증명하지 않습니다. 다음 테스트는 위 메서드에 대해 라인과 분기 커버리지 100%를 달성하지만 아무것도 검증하지 않습니다.
@Test
void runsShippingCost() {
shippingCost(150, false);
shippingCost(20, false);
}
또한 커버리지는 누락된 코드를 드러내지 못합니다. 특정 지역의 회원은 배송비를 내야 한다는 요구사항이 있는데 아무도 구현하지 않았다면, 커버되지 않을 라인 자체가 없습니다. 테스트가 실제로 결함을 감지하는지 측정하는 일반적인 방법은 뮤테이션 테스트입니다.
커버리지를 잘 활용하는 방법
- 숫자만 보지 말고 커버되지 않은 라인을 보세요. 결제 경로의 테스트되지 않은 오류 핸들러가 테스트되지 않은
toString()보다 훨씬 중요합니다. - 변경된 코드의 커버리지로 게이트를 거세요. 예를 들어 “새 코드는 분기 커버리지 80% 이상”처럼요. 전체 비율 기준은 사람들이 사소한 getter까지 테스트하게 만듭니다.
- 100%를 목표로 삼지 마세요. 지표가 목표가 되는 순간 어설션 없는 테스트가 등장합니다.