본문 바로가기
프로젝트 개발 기록/[개발] java | spring

[Gradle] Jacoco 적용하고, Codecov로 테스트 커버리지 관리하기

by HelloJudy 2023. 5. 14.

 
난 지금까지 프로젝트에서 테스트 코드를 열심히 작성하지 못했다.. 크흡
우리 팀 고수 개발자분... 커버리지 80% 이상으로 작성한다고 하셔서.. 리스팩 👏
 
🤖 : 너도 할 수 있지?
🐰 : 8%? ㅎㅎ.. 농담.. (진짜?)
 
이번에 열심히 작성해 보기로 했으니... 파이팅~! (후후.. 어서 와 JUnit은 처음이지?)
 
그리고 프로젝트에서 JacocoCodecov를 적용하려고 한다.
근데 잠깐!!
코드 커버리지는 뭐고, 저 도구들은 뭐야? 그리고 왜 필요해?
 
🤖 : 그말 왜 안 하나 했다.
 
해당 도구들의 사용법을 익히기 전에 개념부터 살펴보자.
 


0. Test Code

테스트 코드는 왜 작성해야 할까.
예전 프로젝트에서는 시간이 부족하다는 이유로 테스트 코드 작성을 소홀히 했다.
하지만 테스트 코드가 없는 코드는 불안정했고, 기능이 확장될수록 예상하지 못한 Side-effect가 발생했다.
테스트 코드를 작성한다면, 아래와 같은 효과를 얻게 된다.
 

  • 제품의 안정성을 높여준다.
  • 기능 추가 및 수정으로 인한 부작용(Side-effect)을 줄일 수 있다.
  • 깔끔하고 재사용성이 좋은 코드 작성을 가능하게 해 준다.

1. Code Coverage

코드 커버리지는 소프트웨어의 테스트 케이스가 얼마나 충족되었는지를 백분율로 나타내는 지표 중 하나이다. 테스트를 진행하였을 때 ‘코드 자체가 얼마나 실행되었느냐’는 것이고, 이는 수치를 통해 확인할 수 있다.

 

[ 코드 커버리지 측정 기준 ]

코드 커버리지는 화이트 박스 테스트(White-box test)를 통해 측정된다.
 
🐰 : 화이트 박스가 모냥! 근데 화이트 있으면 블랙도 있겠네?
 

1) 화이트 박스 테스트(White-box test)

  • 응용 프로그램의 내부 구조와 동작을 검사하는 테스트 방식
  • 소프트웨어 내부 소스 코드를 테스트하는 기법
  • 개발자 관점의 단위 테스트 방법

 

2) 블랙 박스 테스트(Black-box test)

  • 소프트웨어의 내부 구조나 작동 원리를 모르는 상태에서 동작을 검사하는 방식
  • 올바른 입력과 올바르지 않은 입력을 입력하여 올바른 출력이 나오는지 테스트하는 기법
  • 사용자 관점의 테스트 방법

 
🐰 : 그래 코드 커버리지가 뭔지는 알겠어. 근데 왜 중요해? 왜 필요해?
🤖 : 테스트 코드의 중요성은 이제 알겠지? 그러면 개발자가 테스트 코드를 작성하다 보면 놓치는 부분이 있을 거야. 그런 부분을 커버리지를 통해 확인하고 개선할 수 있도록 도와줘!
 
 

[ Java Code Coverage ]

현재 Java, Spring boot 프로젝트를 진행하고 있다.
자바 코드 커버리지 분석 도구에는 Cobertura, Jacoco, Clover 등이 있다.
그중 Jacoco를 사용한 이유는 레퍼런스가 다양하기 때문이다.
 
기술을 처음 도입할 때 여러 가지 장단점을 비교하고 선택하는데, 그중 레퍼런스 유무도 중요한 요소라고 생각한다.
 
또한 코드 커버리지 결과를 보기 좋도록 파일 형태로 저장할 수 있고, Report를 생성할 수 있다.
 


2. JaCoCo 적용

 

⚙️ 프로젝트 환경

  • java : 11
  • Spring Boot : 2.7.11
  • 멀티 모듈 프로젝트

 

1) Jacoco 플러그인 추가

 

  • build.gradle (멀티 모듈이 아닐 때)

테스트 결과 report 경로를 변경할 수도 있다.

  • toolVersion : Jacoco의 jar 버전
    • 2023.05.15 기준 가장 최신 버전으로 설정했다.
  • reportsDir : Jacoco report 결과물 디렉터리
plugins {
  id 'jacoco'
}

jacoco {
  // JaCoCo 버전
  toolVersion = '0.8.10'

//  테스트결과 리포트를 저장할 경로 변경
//  default는 "$/jacoco"
//  reportsDir = file("$buildDir/customJacocoReportDir")
}

 

  • build.gradle (멀티 모듈)

이때는 모든 모듈 테스트에 Jacoco를 적용하기 위해서 설정을 subprojects 내에서 해야 한다.

subprojects {
    ...
    
    apply plugin: 'jacoco'
    
    jacoco {
        toolVersion = "0.8.10"
    }
}

 
 

2) Task 설정

 
Jacoco 플러그인에는 두 가지 Task가 있다.
 

✔️ jacocoTestReport

바이너리 커버리지 결과를 사람이 읽기 좋은 형태의 report로 저장하는 Task

 
Jacoco에서 생성한 바이너리 커버리지 결과는 test.exec 파일이다. 이것을 읽을 수 있도록 jacocoTestReport html, xml, csv 파일을 얻을 수 있다.
 

  • html: 사람이 쉽게 눈으로 확인
  • xml, csv: SonarQube 등을 연동하기 위해 사용

SonarQube 연동할 예정이라서 html, xml을 만들어줬다.

subprojects {
    ...
    
    jacocoTestReport {
      reports {
        // 원하는 리포트를 켜고 끌 수 있습니다.
        html.enabled true
        xml.enabled true
        csv.enabled false

    //  각 리포트 타입 마다 리포트 저장 경로를 설정할 수 있습니다.
    //  html.destination file("$buildDir/jacocoHtml")
    //  xml.destination file("$buildDir/jacoco.xml")
    	
        // 다음 커버리지 기준 테스트가 실행되길 원한다면 추가하자.
        // finalizedBy 'jacocoTestCoverageVerification'
      }
    }
}

 
생성된 html 파일을 열어보면 다음과 같다.

html 파일

 

  • 커버리지 측정 대상에서 제외

일부 클래스를 제외할 수 있다.

jacocoTestReport {
    ...
    
    afterEvaluate {
        classDirectories.setFrom(
                    files(classDirectories.files.collect {
                        fileTree(dir: it, excludes: [
                                "**/*Application*",
                                "**/*Config*",
                                "**/*Dto*",
                                "**/*Request*",
                                "**/*Response*",
                                "**/*Interceptor*",
                                "**/*Exception*"
                        ] + Qdomains)
                    })
    }
}

 
 

✔️ jacocoTestCoverageVerification

내가 원하는 커버리지 기준을 만족하는지 확인해 주는 Task

 
이 task를 설정하면 프로젝트에서 설정한 커버리지 기준이 충족되지 않으면 빌드가 실패된다.
 

subprojects {
    ...
    
    jacocoTestCoverageVerification {
            violationRules {
                rule {
                    enabled = true
                    element = 'CLASS'
                    // includes = []

                    limit {
                        counter = 'LINE'
                        value = 'COVEREDRATIO'
                        minimum = 0.60
                    }

                    excludes = []
                }

                rule {
                    ...
                }
            }
        }
 }

 
 

3) test task 실행 시 JaCoCo task 실행하도록 설정

 
직접 해당 테스크를 실행시킬 수도 있지만 번거롭다.

 
그래서 아래 finalizedBy로 추가하여 test task가 실행될 때마다 jacoco task가 실행되도록 한다.
jacocoTestReport Task 마지막에 finalizedBy 'jacocoTestCoverageVerification' 를 추가했다면 커버리지 기준 Task는 여기에 추가하지 않아도 된다.
 

subprojects {
    ...

    test {
            useJUnitPlatform()
            finalizedBy jacocoTestReport
        }
}

 


3. Codecov 적용

1) 회원가입 및 등록

 
Codecov 사이트에 들어가서 github로 회원가입한다.
 

회원가입하면 여러 레포가 보이는데 레포 중 하나를 누르니 적용하는 방법이 나온다.
난 우리 팀 레포에 추가할 거다!

아잇 친절해~ 따라 해 보자

 

2)  Github secrets에 Token 추가

 

  • Settings > Secrets and variables에서 CODECOV_TOKEN을 추가한다.

 

3) Github Actions workflow에 추가

 
기존에 설정된 CI workflow에 코드를 추가해 보자.
우선 기존 Gradle build에서 test를 추가해 줬다.
test를 실행해야 Jacoco에서 report를 생성한다.

      - name: Setup Gradle
        uses: gradle/gradle-build-action@v2
        with:
          arguments: build test
          cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }}

 
그리고 Jacoco가 생성한 report를 Codecov에 업로드하자!
 
이때 file은 report 경로이고, 우리 프로젝트에 맞는 경로로 설정했다.
(어우.. 멀티모듈인걸 깜빡하고 경로 때문에 헤맸다.. ㅠㅠㅠㅠㅠㅠ)

      - name: Upload coverage reports to Codecov
        uses: codecov/codecov-action@v3
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: '**/build/jacoco/index.xml'

 

 


자자.. 이제 테스트 코드도 열심히 작성하렴 정윤? ㅎㅎ... 


📌 Reference

반응형

댓글