JVM

테스트 커버리지와 JaCoCo

kyoulho 2024. 7. 27. 18:56

테스트 커버리지란 소프트웨어의 테스트 수준이 충분한지 표현할 수 있는 지표 중 하나이다.
많은 코드 커버리지 도구가 있으며, 가장 보편적으로 사용되는 Jacoco에 대해 다루도록 한다.

 

JaCoCo 개요


JaCoCo(Java Code Coverage)는 Java 애플리케이션의 코드 커버리지를 측정하기 위한 도구이다. 코드 커버리지는 테스트된 코드의 비율을 나타내며, 소프트웨어 테스트의 효율성을 평가하는 중요한 지표이다.

JaCoCo의 주요 기능

  • 커버리지 측정: 테스트 코드 실행 시 실행된 코드 라인, 분기, 메소드 등을 측정한다.
  • 리포트 생성: HTML, XML, CSV 형식으로 커버리지 리포트를 생성하여 시각화된 데이터를 제공한다.
  • 검증 규칙 설정: 특정 커버리지 목표를 설정하고 이를 충족하는지 확인할 수 있다.

 

Gradle 빌드 라이프사이클과 JaCoCo의 관계

1. Initialization 단계

프로젝트 설정 및 초기화가 이루어지는 단계이다.

이 단계에서는 JaCoCo가 특별한 작업을 수행하지 않는다.

2. Configuration 단계

빌드 스크립트의 태스크와 플러그인 설정이 이루어지는 단계이다.

JaCoCo 관련 태스크(jacocoTestReport, jacocoTestCoverageVerification 등)가 구성된다. JaCoCo의 설정이 이 단계에서 이루어지며, 이후 테스트와 리포트 생성 과정에서 사용된다.

3. Execution 단계

실제로 설정된 태스크가 실행되는 단계이다.

test 태스크가 실행되면서 JaCoCo 에이전트가 코드 커버리지 데이터를 수집한다.
jacocoTestReport 태스크가 실행되어, 수집된 커버리지 데이터를 분석하고 리포트를 생성한다.
jacocoTestCoverageVerification 태스크가 설정된 커버리지 기준을 검증한다.
check 태스크가 전체 빌드의 성공 여부를 결정한다.

 

Gradle 설정 파일

plugins {
    id("org.springframework.boot") version "3.3.2"
    id("io.spring.dependency-management") version "1.1.6"
    kotlin("plugin.jpa") version "1.9.24"
    kotlin("jvm") version "1.9.24"
    kotlin("plugin.spring") version "1.9.24"
    jacoco
}

... 생략 ...

// JaCoCo 플러그인 버전 설정
jacoco {
    toolVersion = "0.8.7" // 사용할 JaCoCo 버전 지정
}

// 테스트 실행 후 JaCoCo 리포트 생성 태스크 자동 실행 설정
tasks.test {
    finalizedBy(tasks.jacocoTestReport) // 테스트 실행 후 jacocoTestReport 태스크를 자동으로 실행
}

// JaCoCo 리포트 생성 설정
tasks.jacocoTestReport {
    dependsOn(tasks.test) // jacocoTestReport 태스크가 테스트 실행 이후에 실행되도록 설정
    reports {
        xml.required.set(true) // XML 형식 리포트 생성
        csv.required.set(false) // CSV 형식 리포트는 생성하지 않음
        html.outputLocation.set(layout.buildDirectory.dir("jacocoHtml")) // HTML 형식 리포트 저장 위치 설정
    }
}

// 커버리지 검증 규칙 설정
tasks.jacocoTestCoverageVerification {
    violationRules {
        rule {
            limit {
                counter = "INSTRUCTION" // 커버리지 측정 지표: 명령어
                value = "COVEREDRATIO" // 커버된 비율 측정
                minimum = "0.75".toBigDecimal() // 최소 커버리지 비율 설정
            }
            limit {
                counter = "BRANCH" // 커버리지 측정 지표: 분기
                value = "COVEREDRATIO" // 커버된 비율 측정
                minimum = "0.70".toBigDecimal() // 최소 커버리지 비율 설정
            }
        }
    }
}

// Gradle check 태스크가 커버리지 검증 태스크를 의존하도록 설정
tasks.check {
    dependsOn(tasks.jacocoTestCoverageVerification)
}

 

JaCoCo Rule

JaCoCo의 커버리지 룰을 설정할 때는 다양한 요소와 기준을 사용할 수 있다. 각 룰은 다음과 같은 구성 요소로 이루어져 있다.

1. Element type

  • BUNDLE: 패키지 번들. 기본 설정은 전체 프로젝트에 적용됩니다.
  • PACKAGE: 패키지 단위. 예: com.mycompany
  • CLASS: 클래스 단위. 예: com.mycompany.MyClass
  • SOURCEFILE: 소스 파일 단위. 예: com/mycompany/MyFile.java
  • METHOD: 메소드 단위. 예: com.mycompany.MyClass.myMethod()

2. Counter

  • INSTRUCTION: 자바 바이트 코드 명령 수 (기본 설정)
  • BRANCH: 조건문 등의 분기 수
  • LINE: 코드 라인 수
  • COMPLEXITY: 코드 복잡도
  • METHOD: 메소드 수
  • CLASS: 클래스 수

3. Value

  • COVEREDRATIO: 커버된 비율 (0~1)
  • MISSEDRATIO: 커버되지 않은 비율 (0~1)
  • COVEREDCOUNT: 커버된 코드의 수
  • MISSEDCOUNT: 커버되지 않은 코드의 수
  • TOTALCOUNT: 전체 코드의 수

4. Violation Rules

  • rule: 각 규칙을 정의한다.
    • limit: 커버리지 기준을 설정한다.
      • counter: 커버리지 지표 (예: INSTRUCTION, BRANCH)
      • value: 커버리지 비율 측정 유형 (COVEREDRATIO, MISSEDRATIO)
      • minimum/maximum: 커버리지 기준의 최소/최대 값 설정

 

JaCoCo Rule 설정 예제

tasks.jacocoTestCoverageVerification {
    violationRules {
        rule {
            limit {
                counter = "INSTRUCTION" // 명령어 커버리지
                value = "COVEREDRATIO" // 커버된 비율
                minimum = "0.75".toBigDecimal() // 최소 75% 커버리지
            }
            limit {
                counter = "BRANCH" // 분기 커버리지
                value = "COVEREDRATIO" // 커버된 비율
                minimum = "0.70".toBigDecimal() // 최소 70% 커버리지
            }
        }
        rule {
            element = "PACKAGE" // 패키지 단위
            includes = listOf("com.mycompany.*") // 특정 패키지 포함
            limit {
                counter = "LINE" // 라인 커버리지
                value = "TOTALCOUNT" // 총 라인 수
                minimum = "100".toBigDecimal() // 최소 100 라인
            }
        }
        rule {
            element = "CLASS" // 클래스 단위
            includes = listOf("com.mycompany.myapp.MyClass") // 특정 클래스 포함
            limit {
                counter = "METHOD" // 메소드 커버리지
                value = "COVEREDCOUNT" // 커버된 메소드 수
                minimum = "10".toBigDecimal() // 최소 10 메소드
            }
        }
        rule {
            element = "SOURCEFILE" // 소스 파일 단위
            includes = listOf("com/mycompany/myapp/MyFile.java") // 특정 파일 포함
            limit {
                counter = "LINE" // 라인 커버리지
                value = "MISSEDRATIO" // 커버되지 않은 비율
                maximum = "0.10".toBigDecimal() // 최대 10% 미달
            }
        }
    }
}

 

리포트

/build/jacoco-resources/index.html로 접근하면 결과를 확인할 수 있다. 특정 엔티티에 대해서만 테스트 코드를 작성했기 때문에 대부분 커버리지 수치가 낮다.

 
Element를 따라 들어가면 코드마다 정확하게 확인해 볼 수 있다.

'JVM' 카테고리의 다른 글

BDDMockito  (0) 2024.07.28
JUnit  (0) 2024.07.28
StringBuffer vs StringBuilder  (0) 2024.07.22
java.security.invalidKeyException: Illegal Key Size  (0) 2024.05.04
자바 내부 클래스(Inner Classes)  (0) 2024.03.01