난 지금까지 프로젝트에서 테스트 코드를 열심히 작성하지 못했다.. 크흡
우리 팀 고수 개발자분... 커버리지 80% 이상으로 작성한다고 하셔서.. 리스팩 👏
🤖 : 너도 할 수 있지?
🐰 : 8%? ㅎㅎ.. 농담.. (진짜?)
이번에 열심히 작성해 보기로 했으니... 파이팅~! (후후.. 어서 와 JUnit은 처음이지?)
그리고 프로젝트에서 Jacoco와 Codecov를 적용하려고 한다.
근데 잠깐!!
코드 커버리지는 뭐고, 저 도구들은 뭐야? 그리고 왜 필요해?
🤖 : 그말 왜 안 하나 했다.
해당 도구들의 사용법을 익히기 전에 개념부터 살펴보자.
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 파일을 열어보면 다음과 같다.
- 커버리지 측정 대상에서 제외
일부 클래스를 제외할 수 있다.
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
'프로젝트 개발 기록 > [개발] java | spring' 카테고리의 다른 글
[Spring] Spring Security 개념과 처리 과정 👮♀️ (+근데 상황극을 곁들인) (1) | 2023.06.01 |
---|---|
[Java] Gradle.. Gradle 말입니다. 자바 빌드 도구 🐘 (0) | 2023.05.19 |
코딩 컨벤션 설정 | Spotless, Checkstyle ⚙️✨ (1) | 2023.05.09 |
[Java] Mutable, Immutable, 방어적 복사, unmodifiableList (0) | 2023.05.02 |
댓글