테스트 코드를 작성하다 보면 단순히 내가 작성한 테스트 코드의 성공 여부 외에도 얼마나 내가 테스트 코드를 잘 작성하고 있는지 확인할 필요가 있다. 이럴 때 사용하는 것이 코드 커버리지 툴인데, 이러한 툴들을 사용하면 내가 작성한 테스트 코드의 커버리지와 기준에 미치는지 여부 등을 리포트 형식으로 체계적으로 확인할 수 있다.
1. JaCoCo
JaCoCo 는 Java 코드의 커버리지를 체크하는 라이브러리이다. 테스트 코드를 실행하고 그 결과로 html 이나 xml, csv 형식의 리포트를 생성한다. 이 리포트를 통해서 소스 코드 중에서 테스트 되어지는 라인과 분기들에 대해서 확인할 수 있다.
이 글에서는 spring 프로젝트에서 JaCoCo 를 사용하여 테스트 커버리지를 확인하는 방법을 설명할 것이다.
2. JaCoCo example
- plugin 추가
Spring 프로젝트에서 gradle 을 사용하기 때문에 build.gradle 에서 해당 plugin 을 설정해주어 JaCoCo 를 사용할 수 있다. 아래와 같이 build.gradle 의 plugins 에 "id 'jacoco'" 라는 라인을 추가하여 설정한다.
plugins {
id 'jacoco'
}
jacoco 플러그인을 추가하면 gradle 에 jacocoTestReport 와 jacocoCoverageVerification라는 task 가 자동으로 추가된다. jacocoTestReport 는 코드 커버리지에 대한 리포트를 작성하는 task 로 기본적으로 'build/reports/jacoco/test' 에 html 형식의 report 들을 생성한다. jacocoCoverageVerification 은 코드 커버리지의 결과가 설정된 기준을 만족하는지 확인하는 task 이다.
다음과 같이 gradle 명령어를 통해서 task 들을 동작시킬 수 있다.
$ ./gradlew jacocoTestReport
$ ./gradlew jacocoCoverageVerification
- jacoco configuration
jacoco 플러그인은 build.gradle 에서 jacoco 라는 이름의 프로젝트로 설정을 할 수 있다.
jacoco {
toolVersion = "0.8.0" // plugin version
reportsDirectory = layout.buildDirectory.dir('customJacocoReportDir') // jacoco report directory
}
reportsDirectory 의 기본값은 '$buildDir/reports/jacoco' 이다.
- jacocoTestReport task
jacoco 플러그인의 설정외에 jacoco 플러그인의 task 인 jacocoTestReport 의 설정도 할 수 있다. 설정을 통해서 task 는 코드 커버리지 리포트를 다양한 포맷으로 작성할 수 있다.
jacocoTestReport {
reports {
xml.required = false // xml report 는 작성하지 않는다.
csv.required = false // csv report 는 작성하지 않는다.
html.outputLocation = layout.buildDirectory.dir('jacocoHtml') // html report 파일 위치
}
}
- jacocoCoverageVerification
jacocoCoverageVerification task 는 설정을 통해서 사용자가 코드 커버리지의 기준, 규칙을 설정할 수 있다. 아래 예시화 같이 violationRules 안에 여러개의 rule 을 설정할 수 있다.
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.5
}
}
rule {
enabled = false
element = 'CLASS'
includes = ['org.gradle.*']
limit {
counter = 'LINE'
value = 'TOTALCOUNT'
maximum = 0.3
}
}
}
}
- jacoco task configuration
jacoco plugin 은 모든 Test task 에 JacocoTaskExtension 을 추가한다. 그렇기 때문에 각 Test task 에서 jacoco 와 관련된 설정들을 수행할 수 있다.
다음은 test task 에서 jacoco 를 설정하는 예시이다.
test {
jacoco {
destinationFile = layout.buildDirectory.file('jacoco/jacocoTest.exec').get().asFile
classDumpDir = layout.buildDirectory.dir('jacoco/classpathdumps').get().asFile
}
}
JaCoCo task 의 기본설정은 다음과 같이 되어있다.
test {
jacoco {
enabled = true
destinationFile = layout.buildDirectory.file("jacoco/${name}.exec").get().asFile
includes = []
excludes = []
excludeClassLoaders = []
includeNoLocationClasses = false
sessionId = "<auto-generated value>"
dumpOnExit = true
classDumpDir = null
output = JacocoTaskExtension.Output.FILE
address = "localhost"
port = 6300
jmx = false
}
}
- task execution
gradle 에 jacoco 플러그인을 추가하면 jacocoTestReport, jacocoCoverageVerification task 가 추가된다. gradle 에서 이 task 들을 실행시키기 위해서는 다음과 같은 명령어들을 실행하면 된다.
// jacocoTestReport 실행
$ ./gradlew jacocoTestReport
// jacocoCoverageVerification
$ ./gradlew jacocoCoverageVerification
보통은 test 를 실행할 때 jacocoTestReport 와 jacocoCoverageVeirfication 을 같이 실행시킨다. 이를 자동으로 하기 위해서는 build.gradle 에서 task 설정을 통해서 해줄 수 있는데, 아래와 같이 각 task 에서 finalizedBy 키워드로 다음에 실해할 task 를 지정할 수 있다.
test {
// ... test 설정
finalizedBy 'jacocoTestReport'
}
jacocoTestReport {
// ... jacocoTestReport 설정
finalizedBy 'jacocoTestCoverageVerification'
}
위와 같이 build.gradle 에서 설정을 해주면 gradle 로 test 작업을 실행한 후에 jacocoTestReport, jacocoTestCoverageVerification 작업을 순서대로 실행한다.
3. JaCoCo report 형식
jacocoTestReport 의 결과로 생성된 html 파일을 열면 다음과 같이 프로젝트의 패키지와 커버리지 정보가 출력된다. 각 커버리지 항목마다 총 개수와 miss 개수가 표시된다.
패키지 내부로 들어가면 각 패키지가 가지고 있는 소스코드들이 있는데, 소스코드를 클릭하면 테스트가 된 라인과 그렇지 않은 라인을 확인할 수 있다. 테스트가 된 라인은 초록색으로 일부 분기만 테스트 된 라인은 노란색, 테스트 되지 않은 라인은 빨간색으로 표시된다.
[Reference]
- https://docs.gradle.org/current/userguide/jacoco_plugin.html
- https://techblog.woowahan.com/2661/
'Tech > Spring | SpringBoot' 카테고리의 다른 글
[Spring] Spring 과 Tomcat (0) | 2024.06.11 |
---|---|
[SpringBoot] Logging - 3 (log4j2) (1) | 2024.01.13 |
[Spring] lombok - @Builder (0) | 2023.01.30 |
[Spring] MapStruct (1) | 2023.01.08 |
[Spring] ResponseEntity (0) | 2022.12.31 |