개요
코드 커버리지(Code Coverage)는 소프트웨어 테스팅에서 테스트가 소스 코드의 어느 정도를 실행했는지를 측정하는 메트릭이다. 이를 통해 테스트의 효과를 평가하고, 테스트가 코드의 주요 부분을 충분히 검증하고 있는지 판단할 수 있다. 코드 커버리지는 주로 테스트의 완전성을 확인하기 위해 사용되며, 실행되지 않은 코드를 기반으로 추가적인 테스트를 설계할 수 있다.
주요 유형
- 문 커버리지 (Statement Coverage)
코드의 각 문(statement)이 실행되었는지를 측정한다. 예를 들어, if-else
구문에서 if
와 else
블록이 모두 실행되었는지를 확인한다.
- 장점: 간단하고 빠르게 측정할 수 있다.
- 단점: 조건의 모든 경우를 보장하지는 못한다.
- 엣지 커버리지 (Edge Coverage)
Control flow graph하의 모든 Edge들을 참색했는지 측정한다.
- 분기 커버리지 (Branch Coverage)
분기 커버리지는 엣지 커버리지의 한 종류로써, 조건문(if
, switch
)에서 모든 분기(branch)가 실행되었는지를 확인한다. 예를 들어, if (x > 0)
구문에서 x > 0
의 참과 거짓인 경우가 모두 테스트되었는지 확인한다.
- 장점: 모든 논리 경로를 확인하므로 더 높은 신뢰도를 제공한다.
- 단점: 복잡한 조건문이 많을 경우 테스트 설계가 어려워질 수 있다.
- 조건 커버리지 (Condition Coverage)
복합 조건문(예: if (A && B)
)에서 각 개별 조건(A, B)이 참과 거짓인 경우를 테스트했는지 확인한다.
- 함수 커버리지 (Function Coverage)
코드의 모든 함수가 실행되었는지를 확인한다.
- 장점: 모듈 수준에서 테스트 진행 상황을 빠르게 파악할 수 있다.
- 단점: 함수 내부의 세부 동작은 확인하지 못한다.
- 결정 커버리지 (Dicision Coverage
코드의 모든 Return statement가 실행되었는지를 측정한다. Dicision Coverage는 Function Coverage와 Branch Coverage를 동시에 측정한다.
- 경로 커버리지 (Path Coverage)
프로그램 내의 모든 가능한 실행 경로를 테스트한다.
- 장점: 테스트의 완전성이 매우 높다.
- 단점: 실행 경로가 많아질수록 계산량이 급격히 증가한다.
- 루프 커버리지 (Loop Coverage)
루프 구조(for
, while
)에서 모든 반복 경로를 테스트한다.
예를 들어, 루프가 0회, 1회, N회 반복되는 경우를 확인한다.
이외에도, Linear Code Sequence and Jump (LCSAJ)커버리지, Entry/exit coverage, State coverage, Data-flow coverage와 같은 다양한 방법들로 Code Coverage를 측정할 수 있다.
이 Coverage들중에서 Statement coverage(C1)과 Branch or condition coverage(C2)가 제일 많이 사용되는 Coverage로써, 대다수의 상용 프로그램들은 C1혹은 C2커버지리를 바탕으로 Software analysis의 성능을 평가한다. C1과 C2를 같이 사용하면 대다수의 statement를 커버할 수 있어서, 자연스럽게 function, loop, path, state, control flow등등의 Coverage를 평가할 수 있다.
장점
- 테스트가 코드의 중요한 부분을 놓치지 않도록 도와준다.
- 디버깅 과정을 용이하게 한다.
- CI/CD 파이프라인에서 코드 커버리지 도구와 통합하여 자동으로 테스트의 효과를 평가할 수 있다.
- 충분히 테스트된 코드베이스는 유지보수가 용이하며, 안정성을 유지할 가능성이 높다.
한계
- 높은 커버리지가 항상 높은 품질을 의미하지 않는다. 코드가 실행되었더라도 테스트가 코드의 정확한 동작을 검증했는지는 보장하지 않는다. 따라서, 커버리지 수치만을 목표로 삼는 것은 위험할 수 있으므로, 테스트 케이스의 품질과 함께 활용하는 것이 중요하다.
- 높은 커버리지를 달성하려면 많은 테스트 케이스와 시간이 필요하다. 예를 들어서 Full path coverage은 달성하기 힘든 과제이다. 예를 들어서 n개의 decision이 있는 프로그램의 경우 [math]\displaystyle{ 2^n }[/math]개의 path를 측정해야 하기 때문에, 필요한 시간이 기하급수적으로 증가한다.
- 복잡한 프로그램에서는 모든 실행 경로를 테스트하는 것이 현실적으로 어렵다. 일례로, 만약 Full path coverage를 달성할 수 있다는 것은 Halting problem을 해결 가능하다는 말과 동치이다. 즉 현실적으로 프로그램의 모든 것을 측정하는 완전무결한 Code coverage는 존재할 수 없다.
도구
다양한 도구들이 코드 커버리지를 측정하기 위해 사용된다:
- Python:
coverage.py
- Java:
JaCoCo
,Cobertura
- C/C++:
gcov
,LCOV
- JavaScript:
Istanbul
,NYC
- CI/CD 도구: Jenkins, GitLab, CircleCI 등에서 코드 커버리지를 지원한다.