테스트 커버리지
Test Coverage
지표
테스트를 한 바퀴 돌렸을 때 실행된 코드의 비율이고, 어디를 확인하지 않았는지 찾는 데 씁니다.
등장 배경
테스트를 얼마나 써 뒀는지가 오랫동안 말로만 오갔습니다. 충분히 썼다는 답은 사람마다 다른 뜻이었고, 물어본 쪽은 그 답을 확인할 방법이 없었습니다. 스스로도 모르는 경우가 많았습니다. 반년 동안 여러 사람이 붙인 코드에서 어느 부분에 테스트가 붙어 있는지는 기억으로 답할 수 있는 종류가 아닙니다.
그러다 배포 뒤에 환불 금액 계산이 틀려 있는 것이 드러났습니다. 문제가 난 함수에는 테스트가 한 줄도 없었는데, 같은 파일의 옆 함수에는 테스트가 여덟 개 붙어 있었습니다. 여덟 개가 붙은 쪽은 그 코드를 쓴 사람이 테스트를 함께 쓰는 사람이었기 때문이고, 그 차이가 어디에 있는지는 파일을 열어 보기 전에는 보이지 않았습니다. 테스트가 부족했다기보다 어디가 비어 있는지 아무도 몰랐던 것입니다.
그래서 실행된 줄을 기계가 세게 만들었습니다. 이 숫자의 본래 쓸모는 잘 테스트했는지 증명하는 것이 아니라 아직 아무도 실행해 보지 않은 줄을 찾아내는 것입니다. 그 목록이 있으면 어디부터 손댈지가 판단이 아니라 확인이 됩니다.
계산 방법
테스트를 돌리는 동안 어느 줄이 실행됐는지 기록해 두고 전체로 나눕니다.
- 대상 코드의 줄 수를 셉니다. 1,000줄이라면 그것이 분모입니다.
- 테스트를 한 바퀴 돌리면서 실행된 줄을 표시합니다.
- 나눕니다. 720줄이 실행됐으면 72퍼센트입니다.
세는 단위가 여러 가지라는 점을 먼저 알아 둡니다. 줄 단위로 세는 방식이 가장 흔하고, 분기 단위로 세는 방식이 더 엄격합니다. 조건문 하나에서 참인 경우만 테스트하면 줄 커버리지는 100퍼센트가 나오는데 분기 커버리지는 50퍼센트입니다. 거짓인 경우에 무엇이 일어나는지는 아무도 확인하지 않은 상태인데 앞의 숫자는 그것을 가려 줍니다. 그래서 숫자를 인용할 때는 어느 단위인지 함께 말합니다.
분모에서 뺄 것을 정해 두는 일도 계산의 일부입니다. 자동 생성된 코드, 설정 파일, 외부 라이브러리를 그대로 두면 숫자가 실제 상태와 멀어집니다. 생성 코드가 3,000줄 들어 있는 저장소에서는 그것만 빼도 커버리지가 20퍼센트포인트 움직입니다.
측정은 사람이 돌리지 않고 CI/CD에 붙여 자동으로 냅니다. 여기서 기준을 거는 방식이 갈립니다. 전체 비율에 기준을 걸면 오래된 코드 때문에 한동안 아무도 통과하지 못하고, 변경된 코드에만 기준을 걸면 새로 쓰는 부분부터 채워집니다. 뒤쪽이 현실적입니다. 기준을 처음 걸 때는 지금 숫자보다 조금 낮게 잡아 통과되는 상태에서 시작합니다. 처음부터 통과하지 못하는 기준은 하루 만에 꺼집니다.
숫자보다 함께 나오는 목록을 봅니다. 도구는 실행되지 않은 줄을 파일별로 표시해 주는데, 그 목록에서 결제와 권한 판정과 데이터 변경 쪽에 빈 줄이 있으면 비율과 무관하게 그쪽이 먼저입니다. 어느 파일에 몇 줄이 비어 있는지가 다음 주에 무엇을 쓸지 정해 줍니다.
좋은 수준의 기준
절대 기준은 없습니다. 다만 낮은 쪽은 분명히 위험합니다. 30퍼센트인 저장소에서는 함수 하나를 고칠 때 무엇이 함께 깨지는지 알 방법이 없어서, 고치는 일마다 손으로 눌러 보는 시간이 붙습니다.
실무에서 흔히 쓰는 값은 전체 70에서 80퍼센트, 변경된 코드는 80퍼센트입니다. 이 값을 목표로 잡는 이유는 그 위로 올릴수록 드는 품이 급하게 늘기 때문입니다. 남는 20퍼센트는 대개 예외 처리와 오류 경로여서 상황을 만들어 내는 데만 한참이 걸립니다.
영역마다 다른 기준을 두는 편이 낫습니다. 결제 계산과 권한 판정은 95퍼센트를 노리고, 화면에 값을 표시하는 코드는 50퍼센트로 두는 식입니다. 틀렸을 때 되돌리기 어려운 쪽에 숫자를 몰아 줍니다. 이 구분을 문서에 적어 두지 않으면 리뷰에서 같은 실랑이가 매번 벌어집니다.
높다고 안전하지 않은 이유도 기준을 세울 때 함께 봅니다. 실행됐다는 것과 검증했다는 것이 다릅니다. 함수를 부르기만 하고 결과를 확인하지 않는 테스트도 커버리지를 올립니다. 그리고 동시에 두 요청이 들어올 때만 나는 문제나, 데이터가 이미 잘못 쌓여 있을 때만 나는 문제는 이 숫자로 잡히지 않습니다. 그래서 이 값은 합격선이 아니라 하한선으로 씁니다.
실무 용례
"커버리지는 82퍼센트인데 결제 쪽에 안 덮인 줄이 40개입니다."
비율보다 어디가 비었는지가 먼저라는 지적입니다.
"전체 기준 대신 변경분에만 80퍼센트를 걸겠습니다."
오래된 코드 때문에 아무도 통과하지 못하는 상태를 피하려는 제안입니다.
"이 테스트는 함수만 부르고 결과를 안 봅니다. 숫자만 올라갑니다."
실행과 검증을 구분하는 말입니다.
페이지덱이 이 숫자를 처음 붙였을 때 전체가 41퍼센트였습니다. 태오가 안 덮인 줄 목록을 뽑아 보니 절반 이상이 화면에 값을 표시하는 코드였고, 결제와 권한 쪽은 대체로 덮여 있었습니다. 숫자만 보면 위험해 보였지만 실제 위험은 그만큼 크지 않았습니다.
그 뒤로 전체 비율을 올리는 목표는 세우지 않고 변경된 코드에 80퍼센트 기준만 걸었습니다. 반년 뒤 전체는 58퍼센트가 됐고, 배포 뒤 되돌리는 횟수가 월 세 번에서 한 번으로 줄었습니다. 줄어든 원인이 커버리지 자체는 아니었습니다. 새로 쓰는 코드마다 단위 테스트를 함께 쓰는 습관이 생긴 것이 실제 변화였고, 숫자는 그 습관이 유지되는지 보는 창구였습니다.
흔한 오해
이 숫자를 품질 지표로 읽는 경우가 가장 많습니다. 높으면 잘 만든 것처럼 보이니 자연스러운 오해입니다. 하지만 이 값이 답하는 것은 코드가 실행됐는지 하나뿐입니다. 결과를 확인하지 않는 테스트, 애초에 틀린 기댓값을 적어 둔 테스트, 어떤 날은 통과하고 어떤 날은 실패하는 플레이키 테스트도 모두 커버리지를 올립니다. 품질 지표로 쓰면 숫자가 오르는 동안 실제 위험은 그대로인 상태가 만들어집니다.
목표 숫자로 걸면 팀이 알아서 채운다고 보는 것도 흔합니다. 기준이 있으면 움직이니 그렇게 보입니다. 하지만 재는 도구를 목표로 바꾸면 그 도구는 재는 기능을 잃습니다. 90퍼센트를 목표로 받은 팀은 쓰기 쉬운 곳부터 채우고, 쓰기 쉬운 곳은 대개 틀려도 손해가 작은 곳입니다. 결제 함수 하나에 테스트를 제대로 쓰는 데 걸리는 시간이면 표시 코드 스무 줄을 덮을 수 있어서, 숫자만 요구하면 후자가 먼저 채워집니다.
같이 보면 좋은 페이지
마지막 업데이트 2026-09-16