본문 바로가기

개발/Java & Spring

[Java] Java Code Coverage(JaCoCo)를 사용해보자.

소나큐브를 세팅했을 때, 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

 

csbsjy/blog

Contribute to csbsjy/blog development by creating an account on GitHub.

github.com

에서 확인 가능하다