Java 프로젝트의 Code coverage를 측정할 수 있는 JaCoCo 적용해보겠습니다.
SpringBoot : 2.3.4
Gradle : 6.6.1
아래의 내용은 모두 build.gradle에 작성된 것입니다.
plugins {
...
id 'jacoco'
}
jacoco {
toolVersion = '0.8.5'
reportsDir = "${project.reporting.baseDir}/jacoco"
}
jacoco plugin을 추가하면, Gradle/Tasks/verification탭을 보면 jacocoTestReport, jacocoTestCoverageVerification이 생성된 것을 확인할 수 있습니다.
Key | Description |
---|---|
version | JaCoCo 버전으로 기본값은 Latest입니다. |
reportsDir | 테스트결과 리포트 저장 경로입니다. 위에 명시된 값이 기본 경로입니다. |
jacocoTestReport {
reports {
html.enabled true
csv.enabled false
xml.enabled false
}
finalizedBy 'jacocoTestCoverageVerification'
}
jacocoTestReport는 바이너리로 된 커버리지 결과를 사람이 읽기 좋은 형태로 저장합니다. html
, csv
, xml
으로 제공됩니다.
finalizedBy
은 gradle명령어로 "jacocoTestReport를 실행하고, jacocoTestCoverageVerification을 실행시키겠다." 는 의미입니다. report결과가 개발자가 설정한 검증 수준을 넘었는지 확인할 때 사용할 수 있습니다.
jacocoTestCoverageVerification {
violationRules {
rule {
// CLASS단위로, Line CoverateRatio가 60% 이상이어야 한다.
enabled = true
element = 'CLASS'
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.60
}
// CLASS단위로, 빈 줄을 제외한 Line수를 최대 200으로 제한한다.
limit {
counter = 'LINE'
value = 'TOTALCOUNT'
maximum = 200
}
}
}
}
Code Coverage를 검증 수준을 정의하는 부분입니다. 설정한 수준에 달성하지 못하면, task는 실패하게 됩니다.
violocationRules
에 다수의 rule
을 정의할 수 있고, 하나의 rule
이라도 만족하지 못하면, task는 실패합니다.
아래는 JaCoCoLimit의 내용을 간략하게 정리한 것입니다.
Coverage의 기준이 되는 값입니다.
BUNDLE
: Default
PACKAGE
CLASS
SOURCEFILE
METHOD
아래의 조건으로 코드를 수치화 합니다.
INSTRUCTION
: Default, Java 바이트 코드 명령 수 입니다.
LINE
: 빈 줄을 제외한 라인 수
BRANCH
: 조건 문 등의 분기 수
CLASS
: 클래스 수
METHOD
: 메소드 수
COMPLEXITY
: 복잡도(계산법)
counter
로 수치화 된 값이 maximum/minimum
에 충족하는지 확인하기 위한 조건입니다.
COVEREDRATIO
: Default, 커버된 비율로 0~1
사이의 double값으로 1이 100%입니다.
MISSEDRATIO
: 커버되지 않은 비율
TOTALCOUNT
: 전체 개수
MISSEDCOUNT
: 커버되지 않은 수
COVEREDCOUNT
: 커버된 수
이 값은 BigDecimal
로 개발자가 지정한 유효자리까지만 표시되고, 자리수가 넘어가면 반올림 됩니다.
예를들어, minimum = 0.80
이면, 0.7이 아닌 0.74로 표시됩니다.
test {
useJUnitPlatform()
finalizedBy 'jacocoTestReport'
}
useJUnitPlatform()
은 JUnit을 사용하겠다고 Gradle에 알려주는 것으로 JUnit으로 Test Code를 작성했을 경우 필수입니다.
먼저, jacocoTestReport
에서도 finalizedBy 'jaCocoTestCoverageVerification'을 선언했기 때문에 finalizedBy
는 테스트가 수행될 때마다 report를 생성하고, Code coverage 검증을 수행하는 설정입니다.
jacocoTestCoverageVerification {
violationRules {
rule {
...
excludes = [
'*.common.*'
]
}
}
}
excludes
로 테스트에서 제외할 패키지/클래스를 설정할 수 있습니다.
./gradlew clean test
> Task :test
2020-10-07 00:22:34.778 INFO 3988 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
> Task :jacocoTestCoverageVerification FAILED
[ant:jacocoReport] Rule violated for class com.sixhustle.jacoco.Application: lines covered ratio is 0.33, but expected minimum is 0.80
[ant:jacocoReport] Rule violated for class com.sixhustle.jacoco.JacocoController: lines covered ratio is 0.50, but expected minimum is 0.80
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':jacocoTestCoverageVerification'.
> Rule violated for class com.sixhustle.jacoco.Application: lines covered ratio is 0.33, but expected minimum is 0.80
Rule violated for class com.sixhustle.jacoco.controller.JacocoController: lines covered ratio is 0.50, but expected minimum is 0.80
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 4s
7 actionable tasks: 7 executed
./gradlew clean test
명령어로 test > jacocoTestReport > jacocoTestCoverageVerification순으로 task가 실행됩니다.
위는 Test수행 결과로 Lines covered ratio가 0.33인데, 기대하는 minimum ratio는 0.80이라 Test Fail(Build Failed)한 예제입니다.
build.gradle 전체 파일입니다.
plugins {
id 'org.springframework.boot' version '2.3.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
id 'jacoco'
}
group = 'com.sixhustle'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
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()
finalizedBy 'jacocoTestReport'
}
jacoco {
toolVersion = '0.8.5'
}
jacocoTestReport {
reports {
html.enabled true
csv.enabled false
xml.enabled false
}
finalizedBy 'jacocoTestCoverageVerification'
}
jacocoTestCoverageVerification {
violationRules {
rule {
enabled = true
element = 'CLASS'
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.80
}
}
}
}