소나큐브를 세팅했을 때, jacoco dependency 가 필요했다.
이땐 뭔지 궁금해서 자바 코드 커버리지 라이브러리구나 정도만 알고 넘어갔는데, 백기선님의 더 자바 강의의 바이트코드 파트에서 또 짧게 언급이 되었길래 그 사용법을 정리해보고자 한다.
코드 커버리지란?
직관적으로 느껴지듯 수행되는 테스트가 작성된 코드를 얼마나 커버하는지를 말한다.
예를들어 if ( a == b), if ( a > b), if (a <c ) 세 줄의 코드가 있을 때, 테스트는 a와 b에 특정 값을 할당하여 테스트를 진행할 것이다.
이때 a = 5, b =5 인 테스트, a = 10, b= 5 인 테스트, a =5, b = 10 인 테스트를 모두 수행하면 이 테스트의 코드 커버리지는 100%이다.
샘플 프로젝트 생성
우선, gradle 기반의 spring boot 프로젝트를 생성한다.
예시로 계산기 클래스를 작성했다. 아주 간단한 정수의 사칙연산을 담당하는 클래스이다.
이 클래스는 매우 단순하다. 하지만 문제가 없을까?
생각해보면 문제의 소지가 매우 많다. 먼저, 덧셈과 곱셈한 결과가 int 자료형의 범위를 벗어날 수 있다. 뺄셈의 경우도 마찬가지다.
나눗셈의 경우 b 가 0이 들어오면 Exception 이 터질 것이다.
위의 걱정들을 덜 수 있도록 코드를 살짝 수정해보자.
범위는 반환 타입을 long 으로 변경했고, divide 메서드는 b가 0인 경우 Exception 과 메시지를 함께 던져주었다.
이제 테스트코드를 작성해보자.
대충 모든 메서드를 테스트했으니 괜찮다고 생각할 수 있다.
사람이 작성하는 테스트코드인지라 분명 빼먹을 수 있다.
이럴 때 도움을 받을 수 있는 것이 JaCoCo 이다.
build.gradle 을 다음처럼 작성한다.(https://docs.gradle.org/current/userguide/jacoco_plugin.html 참고)
plugins 의 jacoco 와, jacoco, jacocoTestReport 설정을 추가하면 된다.
jacoco의 버전 지정과 결과 Report 관련 설정이다.
plugins {
id 'org.springframework.boot' version '2.2.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
id 'jacoco'
}
group = 'com.blog'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
jacoco {
toolVersion = "0.8.5"
reportsDir = file("$buildDir/customJacocoReportDir")
}
jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.destination file("${buildDir}/jacocoHtml")
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
기본 세팅은 끝났다.
현 위치에서 ./gradlew clean test jacocoTestReport 를 실행한다.
실행이 끝나면 build/jacocoHtml 에서 index.html 파일을 볼 수 있다. 해당 html 을 열어보자.
main 클래스는 테스트 코드를 작성하지 않았으니 생략하고, domain 패키지의 커버리지가 80 퍼센트인 것을 확인할 수 있다.
메서드까지 타고타고 가보면 위와 같은 결과를 볼 수 있는데, 빨간색으로 쳐져있는 부분이 테스트가 수행되지 않은 부분이다.
다시 테스트코드로 돌아가보면 우리는 b가 0인 부분을 테스트하지 않았다.
해당 부분을 테스트하는 테스트코드를 추가해주자.
다시 ./gradlew clean test jacocoTestReport 후 html에 들어가보면
domain 패키지 코드 커버리지가 100% 된 것을 확인할 수 있다.
모든게 완벽해보이지만 몇가지 걸리는 점이 있다.
1. 코드커버리지가 너무 낮으면 빌드자체가 실패했으면 좋겠다!
위의 요구사항을 충족시키기 위해 추가 설정을 해보려고 한다.
2번 먼저 해결해보자. 현재 프로젝트의 커버리지는 85이다.
90% 이하일 경우 빌드가 실패하도록 다음 코드를 build.gradle 에 추가한다.
(해당 기능은 0.6.3 버전 이상부터만 적용 가능하다.)
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.9
}
}
}
}
위와 같이 작성하면 rule 에 해당하는 조건에 부합하지 않으면 빌드가 실패하게된다.
이때 실행하는 task는 ./gradlew clean test jacocoTestCoverageVerification 이다. 결과를 보면
0.9에 부합하지 않기 때문에 jacocoTestCoverageVerification task를 실패했다는 메시지를 볼 수 있다.
./gradlew clean test jacocoTestCoverageVerification jacocoTestReport 한번에 테스트 > 검증 > 레포트 생성 작업까지 할 수도 있다.
2. 패키지마다 코드 커버리지 limit 을 다르게 적용하고 싶으면? 또, 코드 커버리지를 측정할 수 있는 방식이 여러개인데 조합해서 쓰고 싶으면?
jacocoTestCoverageVerification {
violationRules {
rule {
element = 'CLASS'
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.9
}
includes = [
'**/domain/**'
]
}
}
}
rule 에 몇가지 설정을 추가해줬다.
빌드 후 생성되는 CLASS 파일을 기준으로
limit 내부 요소를 살펴보자. Line 수로 측정한다는 뜻이며 그 외 아래 타입들도 지정이 가능하다.
아래와 같이 변경할 수도 있다.
만약 각각의 패키지별로 다른 코드커버리지 제한을 걸고 싶다면 includes, excludes property를 통해 적용할 수 있다.
본 프로젝트에서 main 패키지는 테스트코드를 따로 작성하지 않았다.
따라서, domain 패키지의 코드커버리지만 제대로 측정하고 싶어서 다음과 같이 작성했다.
이 상태로 실행하면,
메인 메서드의 커버리지는 37%임에도 정상적으로 실행됨을 알 수 있다.
테스트코드 기반으로 한눈에 코드커버리지를 확인할 수 있는 매우 좋은 무료 라이브러리라고 생각된다.
자세한 build.gradle 설정은 https://github.com/csbsjy/blog/jacoco
에서 확인 가능하다
'개발 > Java & Spring ' 카테고리의 다른 글
[기록] JUnit5 자주사용하는 코드 모음 (0) | 2020.04.01 |
---|---|
[Java/Spring] javax.validation @Size vs @NotBlank과 Validation Test (2) | 2020.03.02 |
[Spring boot - webpack] Webpack으로 js파일 모듈화하고 번들링하기 (0) | 2019.07.14 |
[Spring boot] 하나의 클래스로 properties 관리하여 사용하기! (0) | 2019.05.13 |
[Spring boot] HandlerMethodArgumentResolver 로 메소드 파라미터에 대한 공통기능 한번에 수행하기 (2) | 2019.03.10 |