JaCoCo는 자바 코드 커버리지를 체크하는 데에 사용되는 오픈소스 라이브러리입니다.
JaCoCO가 가지는 특징은
- Line, Branch 커버리지 제공.
- 코드 커버리지 결과 → 보기 좋은 파일 형태로 저장. (html, xml, csv 등)
- 설정한 커버리지 기준을 만족하는지 확인 가능.
와 같습니다.
코드 커버리지는 소프트웨어의 테스트 케이스가 얼마나 충족되었는지를 나타내는 지표 중 하나이고 테스트 진행 시 ‘코드 자체가 얼마나 실행되었는냐’는 것을 의미합니다. Jacoco를 이용하면 코드 커버리지를 수치로 확인할 수 있습니다.
먼저 코드의 구조는 크게 구문, 조건, 결정의 구조로 이루어져 있고 코드 커버리지는 이러한 코드의 구조를 얼마나 커버했는지에 따라 측정기준이 나뉩니다.
- 구문 - 라인 커버리지
- 코드 한 줄이 한 번 이상 실행된다면 충족됩니다.
void test(int input) { int id = 1; // 1번 if (input > id) { // 2번 system.out.println("test success"); // 3번 } system.out.println("terminate"); // 4번 }
- 만약 들어온 input 값이 id 값보다 작다면 한 줄을 커버할 수 없기에 75%의 라인 커버리지를 가지게 됩니다.
- 조건
- 모든 조건식의 내부 조건이 true/false를 가지게 되면 충족됩니다.
void test(int inputX, int inputY) { int id = 1; // 1번 if (inputX > id && inputY < id) { // 2번 => 이거 하나 하나의 조건들이 충족되야함. system.out.println("test success"); // 3번 } system.out.println("terminate"); // 4번 }
- 이 경우에서 조건 커버리지는 내부 존재하는 input X > id 와 inputY < id 같은 내부 조건들이 모두 각각 true/false를 만족해야 커버리지를 높일 수 있습니다.
- 결정 - 브랜치 커버리지
- 모든 조건식이 true/false를 가지게 되면 충족됩니다.
void test(int inputX, int inputY) { int id = 1; // 1번 if (inputX > id && inputY < id) { // 2번 system.out.println("test success"); // 3번 } system.out.println("terminate"); // 4번 }
- 이 경우에서 내부 조건이 true인지 false인지 상관없이 2번에서 도출되는 결과가 true/false 이 두가지를 모두 커버하는 경우 결정 커버리지를 충족할 수 있습니다.
⇒ 대표적으로 구문 커버리지(라인 커버리지)가 많이 사용된다. 그 이유는 조건, 브랜치 커버리지의 경우 코드 실행에 대한 테스트보다 로직의 시나리오에 대한 테스트에 더 가깝습니다. 그리고 조건, 결정의 경우에 조건이 존재하지 않는 코드에서는 커버리지의 대상에서 아예 제외되어서 테스트를 하지 않는다는 의미입니다.
하지만 라인 커버리지를 충족시 모든 코드를 테스트 코드가 일단은 커버했다고 말할 수 있습니다.
우리는 조건문이 없어도 코드 커버리지 대상에서 제외되지 않기 위해서 구문 커버리지와 어느 정도의 시나리오에 대한 테스트를 위해서 코드 커버리지와 브랜치 커버리지를 선정해서 사용할 예정입니다.
프로젝트 개발 환경 - JAVA 17 - Spring Boot 2.7.7 - Gradle 7.6 - JaCoCO 0.8.8
JaCoCo 설정법
먼저 플러그인에 jacoco를 추가합니다.
plugins { id 'java' id 'org.springframework.boot' version '2.7.7' id 'io.spring.dependency-management' version '1.0.15.RELEASE' id 'org.asciidoctor.convert' version '1.5.8' id 'jacoco' // 추가한부분 } jacoco { toolVersion = '0.8.8' } // 위에 다른 플러그인 처럼 버전을 함께 쓰고 싶은데 지원이 안되는 듯. // 따로 task로 만들자.
jacoco 플러그인에 2가지 task가 존재합니다.
jacocoTestReport
: 커버리지 결과를 사람이 보기 좋은 형태 html, xml, csv 등으로 출력함.
jacocoTestCoverageVerification
: 내가 설정한 커버리지 기준에 부합하는지를 검사함.
코드 커버리지의 퍼센트를 70퍼센트로 설정했기 때문에 아래와 같이 task를 추가해줍니다.
jacocoTestReport { reports { // 내가 원하는 형태로 출력함. html.enabled(true) xml.enabled(true) csv.enabled(false) } finalizedBy('jacocoTestCoverageVerification') }
먼저 jacocoTestReport task에서 reports를 내가 원하는 형태로 출력할 수 있습니다.
finalizedBy는 해당 task가 끝나면 어떤 태스크를 실행할지 task의 의존관계를 설정해주는 부분입니다. 그래서 해당 task가 끝나면 jacocoTestCoverageVerification task
를 실행하겠다는 의미입니다.
jacocoTestCoverageVerification { violationRules { rule { enabled = true // 해당 룰을 사용할 것인지 아닌지 element = 'CLASS' //룰을 체크할 단위 => 여기선 클래스 단위로 체크. limit { counter = 'BRANCH' // 커버리지 체크 단위 => 여기선 분기별로 커버리지 체크 value = 'COVEREDRATIO' // ratio로 나타내고 아래 설정한걸보면 최소 60% minimum = 0.60 } limit { counter = 'LINE' // 커버리지 체크 단위 => 여기선 라인별로 커버리지 체크 value = 'COVEREDRATIO' // ratio로 나타내고 아래 설정한걸보면 최소 70% minimum = 0.70 } excludes = [ 'com.prgrms.be.intermark.IntermarkApplication' ] } } }
- 이제 커버리지 기준에 부합하는 검사하는 task를 설정해야 하는데 기준에 부합한지 확인하기 위해선 기준을 설정해야 합니다.
- rule 내부에 limit로 제한을 걸어줍니다. (rule은 violactionRuiles 내부에 여러개가 들어갈 수 있습니다.)
- excludes를 통해서 해당 룰을 적용하지 않을 클래스를 지정할 수도 있습니다(패키지 + 클래스명으로 지정).
이렇게 설정하면 test task를 실행할 때 jacoco 만들어준 task를 두개를 다 같이 실행해야 하기 때문에
그걸 쉽게 하기 위해 finalizedBy를 이용해 test가 끝나면 jacocoTestReport
task가 실행되게 설정해줍니다.
test { outputs.dir snippetsDir useJUnitPlatform() finalizedBy('jacocoTestReport') } jacocoTestReport { reports { // 내가 원하는 형태로 출력함. html.enabled(true) xml.enabled(true) csv.enabled(false) } finalizedBy('jacocoTestCoverageVerification') }
이렇게 하면 test task 실행 시에 jacoco task들도 같이 실행됩니다.
이렇게 설정하고 Gradle에서 test task를 더블 클릭하면 커버리지가 부족한 경우
어떤 커버리지가 부족한지 나오게 됩니다.
이것에 대한 report가 설정해준 xml, html로 나오는 것을 볼 수 있습니다.
Jacoco에서 Lombok 어노테이션을 커버리지 측정 시 제외하는 설정.
아래 구문을 가진 config를 패키지 main, test 패키지와 같은 레벨에 위치시켜줍니다.
config.stopBubbling = true lombok.addLombokGeneratedAnnotation = true
아래와 같이 빌더나 다른 어노테이션에 대한 코드 커버리지 측정이 사라진 것을 볼 수 있습니다.
Uploaded by
N2T