Spring boot로 프로젝트 개발을 진행한다면 스프링의 꽃 중 하나인 테스트를 꼭 경험하게 될 것이다.
토비 : 테스트를 작성하지 않을 것이면, 스프링 부트를 왜 사용할까요?
Jacoco Report는 이런 테스트에 대한 Coverage를 체크해주는 도구이다.
예를 들어 테스트에서 클래스 내 각 메서드들을 테스트 했는지?
각 분기에 대해 테스트 했는지?
말 그대로 정보처리기사에 나오는 Test Coverage를 대신 처리해주는 자동화 도구이다.
테스트 방법으로는 인수 테스트, 통합 테스트, 단위 테스트가 존재한다. 우리 프로젝트에서는 인수 테스트는 따로 진행하지 않고 비즈니스 로직에 집중하기 위해서 서비스 레이어를 제외한 각 계층에서 단위 테스트를 진행했다.
하지만, 첫 프로젝트인만큼 중간 중간 테스트를 놓치는 부분도 있었고 스프린트 기간 안에 기능 개발을 위해 테스트를 일부러 하지 않은 경우도 있었다.
이게 독이 될 줄 몰랐다.
Front 측에서 API에 대해 추가 기능 요구나 버그로 인한 수정 요구 시 코드를 수정하는 작업이 발생할 때마다 스프링 컨테이너를 띄워서 일일이 다 확인해야 됐다.
흠.. 어떻게 개선할 수 없을까?.. 그래서 막 리서치하던 중 우아한 기술 블로그에서 소개하는 Jacoco Report를 알게 된 것이다.
그럼, Jacoco Report로 어떻게 개선되는지 한 번 보자.
// Test Coverage
tasks.named('test') {
useJUnitPlatform()
finalizedBy jacocoTestReport
}
jacoco {
toolVersion = "0.8.9"
}
def classDirectoriesFilter = {
project.fileTree(
dir: it,
exclude: [
'**/deepdivers/community/BackendApplication*',
'**/deepdivers/community/**/config/**',
'**/deepdivers/community/**/exception/**',
'**/deepdivers/community/**/dto/**',
'**/deepdivers/community/**/entity/**',
'**/deepdivers/community/**/interceptor/**',
'**/deepdivers/community/**/resolver/**',
'**/deepdivers/community/**/generator/**',
'**/deepdivers/community/global/handler/GlobalExceptionHandler*',
'**/deepdivers/community/infra/aws/s3/properties/**',
'**/deepdivers/community/infra/aws/s3/generator/S3RequestGenerator*',
'**/Q*.class',
'**/*_.class',
'**/generated/**'
]
)
}
jacocoTestReport {
dependsOn test
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect(classDirectoriesFilter)))
}
reports {
xml.required = false
csv.required = false
html.required = true
html.outputLocation = layout.buildDirectory.dir('reports/jacoco/html')
}
finalizedBy jacocoTestCoverageVerification
}
jacocoTestCoverageVerification {
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect(classDirectoriesFilter)))
}
violationRules {
rule {
element = 'CLASS'
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.80
}
limit {
counter = 'BRANCH'
value = 'COVEREDRATIO'
minimum = 0.80
}
limit {
counter = 'METHOD'
value = 'COVEREDRATIO'
minimum = 0.80
}
}
}
}
plugins {
id 'jacoco'
}
jacocoTestReport {
dependsOn test
}
afterEvaluate {
classDirectories.setFrom(...)
}
reports {
xml.required = true
csv.required = false
html.required = true
html.outputLocation = layout.buildDirectory.dir('reports/jacoco/html')
}
finalizedBy jacocoTestCoverageVerification
violationRules {
rule {
element = 'CLASS'
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.80
}
}
}
이제 jacoco report 설정이 끝났다. 결과를 확인해보자!
# ${PROJECT_ROOT}/gradlew clean test
이제 프로젝트의 build/reports 에서 결과를 확인할 수 있다.
우선, 테스트 결과를 보자!

모든 클래스에 대해 성공적으로 테스트가 됐다는 것을 확인할 수 있다!!
이번엔 Coverage까지 보자.

이제 부족한 테스트에 대해 쉽게 확인 할 수 있다.

사진 결과와 같이 분기나 특정 라인, 메서드 별로 테스트 되지 않은 정보를 확인 할 수 있다!
(resetPassword에서 JPA 더티 체킹을 활용하지 못한게 거슬리네..)
report 설정에 의문점이 들었던 부분이 있었다.
jacocoTestCoverageVerification Task이 JacocoReport Task 이후에 실행된다면, jacocoTestCoverageVerification Task에서는 report 정보가 있으므로 굳이 파일 집합을 안만들어 되지 않나?
학습 곡선도 그렇게 높지 않았고 JacocoRepository를 적용하면서 부족한 테스트를 한 눈에 확인 할 수 있었다. 또 CI 작업 시, PR에서 좀 더 가시적으로 test coverage 결과를 볼 수 있어서 좋은 기술을 알게 된 것 같다!