[Testing] 코드 커버리지 (Code Coverage)

나른한 개발자·2023년 2월 20일
1

테스트 커버리지, 코드 커버리지

테스트 커버리지란 시스템 및 소프트웨어에 대해 충분히 테스트가 되었는지를 나타내는 정도이다. 수행한 테스트가 얼마나 테스트 대상을 커버했는지를 나타낸다.

코드 커버리지란 테스트에 의해 실행된 소스 코드의 양을 나타낸 것이다. 즉, 테스트로 코드가 얼마나 covered되었는지 나타내는 것이다. 소스 코드를 기반으로 수행하는 화이트 박스 테스트를 통해 측정된다. 퍼센트 (%)단위로 표시되는데, 커버리지가 100%라고 해서 버그없는 소프트웨어를 보장할수는 없다.

💡 화이트 박스 테스트
소프트웨어 내부 소스 코드의 논리적인 모든 경로를 테스트하여 모듈 내부의 작동을 확인하는 것이다. 모듈을 한번 이상 실행하므로써 수행된다.

+) 블랙 박스 테스트
사용자의 입장에서 구현된 기능이 완전하게 동작되는지를 확인하는 테스트이다. 소프트웨어 내부 코드 및 세부 동작을 알 필요없으며 입력 값에 따라 올바른 출력값이 나오는 지 확인하는 테스트 이다.



필요성

테스트 코드는 발생할 수 있는 모든 시나리오에 대해 작성되어야 하는데, 이때 코드 커버리지는 테스트를 수치화하여 개발자로 하여금 미처 놓친 코드의 구멍을 보완할 수 있게 해준다.



코드 커버리지 기준

구문 커버리지 (Statement Coverage)

라인 커버리지라고도 하며 코드 한줄이 한 번 이상 실행된다면 충족된다.

def foo(a: int):
	print("--function starts--")	# 1번
    if a > 0:	# 2번
    	a //= 2	# 3번
	print("--function ends--")	# 4번

조건에 따른 모든 로직을 확인하기 보다는 모든 코드가 실행되었는지를 확인한다. 위 예시 코드의 경우 a가 음수이면 if문 내부 로직은 실행되지 않기 때문에 4개의 구문 중 3개의 구문만 실행되어 커버리지는 3 / 4 * 100 = 75(%)가 된다.

결정 커버리지 (Decision Coverage)

브랜치 커버리지라고도 하며 조건식이 True/False를 가지는 경우 충족된다.

def foo(a: int, b: int):
	if a > 0 and b > 0:
    	 print("processing")

예를 들어 위 예시 코드와 같은 함수가 있다고 할때 foo(1, 1), foo(1,0)의 경우 조건문에 대해 참/거짓을 만족하는 테스트이기 때문에 결정 커버리지를 만족한다.

조건 커버리지 (Condition Coverage)

모든 내부 조건에 대해 True/False를 가지게 되면 충족된다.

def foo(a: int, b: int):
	if a > 0 and b > 0:
    	 print("processing")

내부 조건이란 if문 내부의 모든 조건들을 의미한다. 예시 코드에서는 a>0 , b>0가 내부 조건이 되며 각각에 대해 True/False를 가지는 테스트가 있다면 충족된다.

예를 들어 테스트 케이스가 foo(1, 0), foo(0, 1) 일 때 a>0, b>0 모두의 내부조건에 대해 True/False 케이스가 존재하므로 조건 커버리지를 만족한다. 하지만 a>0 and b>0 의 조건에서는 False이므로 조건문 내부의 코드는 테스트하지 못한다. 이처럼 조건 커버리지를 만족한다고 해도 구문 커버리지 또는 결정 커버리지를 만족하지 못할 수 있다.




대중적인 코드 커버리지 기준

위 세 가지의 코드 커버리지 기준 중에 가장 많이 사용되는 것은 구문 커버리지이다.
조건 커버리지와 결정 커버리지의 경우 조건문을 만족한다면 코드 커버리지를 만족하기 때문에 만약 조건문이 존재하지 않는 코드의 경우 커버리지의 대상에서 제외해버려 테스트를 하지 않는다.

구문 커버리지의 경우 조건식에 따라 실행되지 않는 코드들도 있기 때문에 모든 시나리오에 대해 테스트한다는 보장은 없지만 전체 코드에 대한 테스트가 커버되었다고는 할 수 있다. 따라서 라인 커버리지를 더 많이 사용한다고 한다.



코드 커버리지 목표 수치 정하기

낮은 커버리지 수치는 배포 시 프로덕트의 큰 부분에서 테스트가 이뤄지지 않았다는 것을 나타내기 때문에 나쁜 코드가 프로덕션으로 넘어갈 확률이 크지만 커버리지가 높다고해서 항상 안정적인 소프트웨어임을 보장하지는 못한다.

오히려 커버리지 수치를 높이기 위해 질낮은 테스트 코드가 양산될 수 있다. 이는 관리해야하는 테스트가 늘어난다는 의미이므로 기술 부채를 키운다. 또한 나쁜 코드가 테스트 상에서 잡히지 않는 경우는 대부분 엣지 케이스를 제대로 다루지 못한 경우인데, 이 경우 코드 커버리지에서는 테스트를 수행한 것으로 집계되어 커버리지 분석만으로 제대로 진단하기 어렵다. 따라서 커버리지 수치를 90%에서 95%로 높이는데에 집중하는 것은 생각보다 의미있는 일이 아닐 수도 있다는 것이다.

모든 소프트웨어에 공통적으로 적용할 수 있는 "이상적인" 커버리지 수치라는 것은 없다. 구글에서는 커버리지 수치가 60%면 용인되는 수준, 75%이면 칭찬할만한 수준, 90%면 모범적인 수준으로 보고있지만 전사적으로 강제하는 기준은 아니며 비즈니스 요구에 맞춰 얼마를 달성할 것인지 정하도록 격려한다고 한다.

따라서 항상 이상적인 코드 커버리지 수치를 달성할 필요는 없지만 어느 수준의 테스트를 필요로 하는지는 다음의 요소들이 고려되어야 한다.

  • 코드 변경의 빈도수
  • 비즈니스에 영향을 미치는 정도
  • 코드의 복잡도 및 생애

목표치를 정할 때는 테스트가 이뤄지지 않는 범위에서 발생할 수 있는 잠재 위험을 감수할 것인지 아닌지를 판단하는 것이 중요하다.





참고
코드 커버리지 모범 사례
코드 분석 도구 적용기 - 1편, 코드 커버리지(Code Coverage)가 뭔가요?
코드 커버리지(Code Coverage)란?

profile
Start fast to fail fast

0개의 댓글