macOS
IntelliJ IDEA
Java 17
Spring Boot 3.x
Groovy
๐ JaCoCo
JaCoCo ํ๋ฌ๊ทธ์ธ ์ ์ฉ
JaCoCo :: Maven Plugin์์ JaCoCo์ ์ต์ ๋ฒ์ ์ ํ์ธํ ์ ์๋ค.
์ฒ์์๋ build.gradle์ JaCoCo ํ์คํฌ๋ฅผ ์ค์ ํ์ผ๋, ๋๋ฌด ๋ณต์กํด์ ธ์ ๋ณ๋๋ก jacoco.gradle ํ์ผ์ ์์ฑํ์ฌ ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ์๋ค.
// build.gradle
apply from: 'jacoco.gradle'
// jacoco.gradle
subprojects {
apply plugin: 'jacoco' // Jacoco Plugin ์ถ๊ฐ
jacoco {
toolVersion = '0.8.12'
// reportsDir = ${project.reporting.baseDir}/jacoco
}
test {
useJUnitPlatform()
}
}
JaCoCo ํ๋ฌ๊ทธ์ธ์ ์ถ๊ฐํ๊ณ ๋ฒ์ ์ ์ค์ ํ ๋ค gradle์ ์๋ก๊ณ ์นจ ํ๋ฉด JaCoCo ์์กด์ฑ์ด ์ถ๊ฐ๋๋ฉด์ ๊ฐ ๋ชจ๋์ Tasks/verification์ JaCoCo์ Task๊ฐ ์ถ๊ฐ๋๋ค.
reportsDir์ ๋ณ๋๋ก ์ค์ ํ์ง ์์ ๊ฒฝ์ฐ, ${project.reporting.baseDir}/jacoco ๊ฐ ๊ธฐ๋ณธ๊ฒฝ๋ก๋ก ์๋ ์ค์ ๋๋ค.
๋ณด๊ณ ์ ํ์ผ ์ค์
๋ณด๊ณ ์ ํ์ผ์ ์์ฑํ๊ธฐ ์ํด jacoco.gradle์ ์๋ ๋ด์ฉ์ ์ถ๊ฐํด ์ค๋ค.
jacocoTestReport {
reports {
html.required.set(true)
xml.required.set(false)
csv.required.set(false)
}
}
JaCoCo๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฐ์ด๋๋ฆฌ ๋ณด๊ณ ์์ธ exec ํ์ผ์ ์์ฑํ๋ค. ์ด๋ ์ฌ๋์ด ์ฝ์ ์ ์๊ธฐ ๋๋ฌธ์ Jacoco Plugin์ jacocoTestReport ํ์คํฌ๋ฅผ ํตํด exec ํ์ผ์ XML, CSV, HTML ํ์ผ ํํ๋ก ์ถ๋ ฅํ ์ ์๋ค.
XML๊ณผ CSV ํ์ผ์ ์์ค ์ฝ๋ ๋ถ์์ ์ํด ์๋ํ๋ธ ๋ฑ๊ณผ ์ฐ๋ํ ๋ ์ฌ์ฉ๋๋ฉฐ, HTML ํ์ผ์ ์ฌ๋์ด ์ง์ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ํ์ธํ ๋ ์ฌ์ฉ๋๋ค.
subprojects {
apply plugin: 'jacoco'
jacoco {
toolVersion = '0.8.12'
}
test {
useJUnitPlatform()
}
jacocoTestReport {
reports {
html.required.set(true)
xml.required.set(false)
csv.required.set(false)
}
}
}
์ปค๋ฒ๋ฆฌ์ง ๊ธฐ์ค ์ค์
JaCoCo ํ๋ฌ๊ทธ์ธ์์๋ jacocoTestCoverageVerification ํ์คํฌ๋ก ์ต์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ์์ค์ ์ค์ ํ ์ ์๊ณ , ๊ธฐ์ค์ ํต๊ณผํ์ง ๋ชปํ ๊ฒฝ์ฐ ํ์คํฌ๊ฐ ์คํจํ๋๋ก ๋ง๋ค ์ ์๋ค.
jacocoTestCoverageVerification์ violationRules ๋ฉ์๋๋ฅผ ํตํด ์ปค๋ฒ๋ฆฌ์ง ๊ธฐ์ค์ ์ค์ ์ ์ ์ํ ์ ์๊ณ , ๊ฐ๊ฐ์ ๋ฃฐ์ ๋ํ ์ค์ ์ violationRules ๋ฉ์๋์ ์ ๋ฌํ rule ๋ฉ์๋๋ฅผ ํตํด ์ ์ํ ์ ์๋ค.
jacocoTestCoverageVerification {
violationRules {
rule {
enabled = true
element = "CLASS"
limit {
counter = "LINE"
value = "COVEREDRATIO"
minimum = 0.8
}
excludes = [
'*.*Application'
]
}
rule {
// ์ฌ๋ฌ๊ฐ์ ๊ท์น์ด ๊ฐ๋ฅํ๋ค
}
}
}
- enabled
๊ท์น์ ํ์ฑํ ์ฌ๋ถ๋ฅผ boolean์ผ๋ก ๋ํ๋ธ๋ค. ๊ธฐ๋ณธ๊ฐ์ true์ด๋ค. - element
์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ฒดํฌํ ๊ธฐ์ค(๋จ์)์ ์ค์ ํ๋ค. ๊ธฐ๋ณธ๊ฐ์ BUNDLE์ด๋ค.- BUNDLE: ํจํค์ง ๋ฒ๋ค (์ ์ฒด ํ๋ก์ ํธ)
- CLASS: ํด๋์ค
- GROUP: ๋ ผ๋ฆฌ์ ๋ฒ๋ค ๊ทธ๋ฃน
- METHOD: ๋ฉ์๋
- PACKAGE: ํจํค์ง
- SOURCEFILE: ์์ค ํ์ผ
- includes
ํด๋นํ๋ rule์ ์ ์ฉ๋์์ package ์์ค์ผ๋ก ์ ์ํ ์ ์๋ค. ๊ธฐ๋ณธ๊ฐ์ ์ ์ฒด package์ด๋ค. - counter
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ธก์ ํ ๋ ์ฌ์ฉ๋๋ ์งํ์ด๋ค. ๊ธฐ๋ณธ๊ฐ์ INSTRUCTION์ด๋ค.- LINE: ๋น ์ค์ ์ ์ธํ ์ค์ ์ฝ๋์ ๋ผ์ธ ์, ๋ผ์ธ์ด ํ ๋ฒ์ด๋ผ๋ ์คํ๋๋ค๋ฉด ์คํ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ
- BRANCH: ์กฐ๊ฑด๋ฌธ ๋ฑ์ ๋ถ๊ธฐ ์
- CLASS: ํด๋์ค ์, ๋ด๋ถ ๋ฉ์๋๊ฐ ํ ๋ฒ์ด๋ผ๋ ์คํ๋๋ค๋ฉด ์คํ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ
- METHOD: ๋ฉ์๋ ์, ๋ฉ์๋๊ฐ ํ ๋ฒ์ด๋ผ๋ ์คํ๋๋ค๋ฉด ์คํ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ
- COMPLEXITY: ๋ณต์ก๋
- INSTRUCTION: ์๋ฐ ๋ฐ์ดํธ์ฝ๋ ๋ช ๋ น ์
- value
์ธก์ ํ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ด๋ค ๋ฐฉ์์ผ๋ก ๋ณด์ฌ์ค ๊ฒ์ธ์ง๋ฅผ ๋งํ๋ฉฐ limit ๋ฉ์๋๋ฅผ ํตํด ์ง์ ํ ์ ์๋ค. ๊ธฐ๋ณธ๊ฐ์ COVEREDRATIO์ด๋ค.- COVEREDCOUNT: ์ปค๋ฒ๋ ๊ฐ์
- COVEREDRATIO: ์ปค๋ฒ๋ ๋น์จ, 0-1 ์ฌ์ด์ ์ซ์๋ก 1์ด 100%
- MISSEDCOUNT: ์ปค๋ฒ๋์ง ์์ ๊ฐ์
- MISSEDRATIO: ์ปค๋ฒ๋์ง ์์ ๋น์จ, 0-1 ์ฌ์ด์ ์ซ์๋ก 1์ด 100%
- TOTALCOUNT: ์ ์ฒด ๊ฐ์
- minimum
counter ๊ฐ์ value์ ๋ง๊ฒ ํํํ์ ๋ ์ต์๊ฐ์ ๋งํ๋ฉฐ limit ๋ฉ์๋๋ฅผ ํตํด ์ง์ ํ ์ ์๋ค. ์ด ๊ฐ์ ํตํด jacocoTestCoverageVerification์ ์ฑ๊ณต ์ฌ๋ถ๊ฐ ๊ฒฐ์ ๋๋ค.
ํด๋น ๊ฐ์ BidDecimal ํ์ ์ด๊ณ ํ๊ธฐํ ์๋ฆฟ์๋งํผ value๊ฐ ์ถ๋ ฅ๋๋ค. ๋ง์ฝ ์ปค๋ฒ๋ฆฌ์ง ์ค์ ์ 80%์ ์ํ์ ๋, 0.80์ด ์๋ 0.8 ์ ๋ ฅ ์ ์ปค๋ฒ๋ฆฌ์ง๊ฐ 0.87์ด์ด๋ 0.8๋ก ํ์๋๋ค. - excludes
์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ธก์ ํ ๋ ์ ์ธํ ํด๋์ค๋ฅผ ์ง์ ํ ์ ์๋ค. ํจํค์ง ๋ ๋ฒจ์ ๊ฒฝ๋ก๋ก ์ง์ ํ์ฌ์ผ ํ๊ณ ๊ฒฝ๋ก์๋ *์ ?๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
JaCoCo ํ๋ฌ๊ทธ์ธ ํ์คํฌ ์์ ์ค์
ํ ์คํธ → ๋ณด๊ณ ์ ์์ฑ
JaCoCo ํ๋ฌ๊ทธ์ธ์ User Guide ๋ฌธ์๋ฅผ ๋ณด๋ฉด ํ ์คํธ๋ ๋ณด๊ณ ์ ์์ฑ(jacocoTestReport ํ์คํฌ) ์ ์ ์คํ๋์ด์ผ ํ๋๋ฐ, jacocoTestReport ํ์คํฌ๋ ํ ์คํธ ํ์คํฌ์ ์์กดํ์ง ์์ ํญ์ jacocoTestReport๋ฅผ ์์ฑํ๊ฑฐ๋ ๋ณด๊ณ ์๋ฅผ ์์ฑํ๊ธฐ ์ ์ test ํ์คํฌ๋ฅผ ๋ช ์์ ์ผ๋ก ์คํํด์ผ ๋๋ค๊ณ ํ๋ค.
๋ฐ๋ผ์ ํ ์คํธ๊ฐ ๋๋๋ฉด ๋ณด๊ณ ์๋ฅผ ์์ฑํ๋ ํ์คํฌ๊ฐ ์๋์ผ๋ก ์คํ๋๋๋ก jacoco.gradle์ ์๋ ๋ด์ฉ์ ์ถ๊ฐํด ์ค๋ค.
test {
useJUnitPlatform()
finalizedBy 'jacocoTestReport'
}
finalizedBy ์ฌ์ฉ ์ ์ด์ ํ์คํฌ(test)์ ์ฑ๊ณต ์ฌ๋ถ์ ๊ด๊ณ์์ด ๋ช ์ํ ํ์คํฌ(jacocoTestReport)๋ฅผ ์ด์ด ์คํํ๋๋ก ์ค์ ํ ์ ์๋ค.
๋ณด๊ณ ์ ์์ฑ → ์ปค๋ฒ๋ฆฌ์ง ๊ธฐ์ค ๋ง์กฑ ์ฌ๋ถ ํ์ธ
๋ณด๊ณ ์๋ฅผ ์์ฑํ๋ jacocoTestReport์ ์ค์ ํ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ๋ง์กฑํ๋์ง ํ์ธํ๋ jacocoTestCoverageVerification์ ์์๋ฅผ ์ง์ ํ์ง ์์ jacocoTestCoverageVerification ํ์คํฌ๊ฐ jacocoTestReport ํ์คํฌ๋ณด๋ค ๋จผ์ ์คํ๋์ด ์ค์ ํ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ํต๊ณผํ์ง ๋ชปํ๋ฉด gradle ๋น๋๊ฐ ๋ฉ์ถ๊ฒ ๋๋ค. ๊ทธ๋ ๊ฒ ๋๋ฉด jacocoTestReport ํ์คํฌ๋ ์คํ๋์ง ์๊ฒ ๋๊ณ , ๋ณด๊ณ ์๊ฐ ์์ฑ๋์ง ์์ ์ด์ ํ ์คํธ์์ ์์ฑ๋ ๋ณด๊ณ ์๋ฅผ ๋ณด๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ ์ ์๋ค.
jacocoTestReport {
reports { ... }
afterEvaluate { ... }
finalizedBy 'jacocoTestCoverageVerification'
}
๋ฐ๋ผ์ finalizedBy ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ test → jacocoTestReport → jacocoTestCoverageVerification ์์๋ก ํ์คํฌ๋ฅผ ์คํํ๋ฉด ๋ชจ๋ ์กฐ๊ฑด์ ๋ง์กฑํ ์ ์๋ค.
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ถ์ ๋์ ์ ์ธ
ํ ์คํธ๋ฅผ ํ์ง ์์๋ ๋๋ ์์ธ ํด๋์ค, DTO ํด๋์ค ๋ฑ์ ํฌํจํ์ฌ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ๊ณ์ฐํ๋ฉด ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๊ฐ ๋ฎ๊ฒ ๋์ค๊ธฐ ๋๋ฌธ์ ํด๋น ํน์ ํด๋์ค๋ค์ ๋ถ์ ๋์์์ ์ ์ธํ ์ ์๋ค.
๋ณด๊ณ ์์ ํฌํจ๋ ํน์ ํด๋์ค · ํจํค์ง ์ ์ธ
jacocoTestReport ํ์คํฌ์์ ๋ณด๊ณ ์์ ํ์๋๋ ํด๋์ค ์ค ์ผ๋ถ๋ฅผ ์ ์ธํ ์ ์๋ค. ์ ์ธ ๋์ ํ์ผ๋ค์ Ant ์คํ์ผ ํจํด์ผ๋ก ์์ฑํ๋ค.
jacocoTestReport {
reports {
// ...
}
afterEvaluate {
classDirectories.setFrom(
files(classDirectories.files.collect {
fileTree(dir: it, excludes: [
'**/common/**',
'**/config/**',
'**/*Application*',
'**/*DetailService*',
])
}))
}
finalizedBy 'jacocoTestCoverageVerification'
}
๐ jacocoTestReport์์๋ ๊ฒฝ๋ก๋ฅผ '/'๋ก ๊ตฌ๋ถํ๊ณ , ์๋์ jacocoTestCoverageVerification์ ๊ฒฝ๋ก๋ฅผ '.'๋ก ๊ตฌ๋ถํ์ ๋ ์ฌ๋ฐ๋ฅด๊ฒ ์ ์ฉ๋๋ค.
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๊ฒ์ฆ ๋์์์ ํน์ ํด๋์ค · ํจํค์ง ์ ์ธ
jacocoTestCoverageVerification ํ์คํฌ์์๋ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ๋ง์กฑํ๋์ง ํ์ธํ ๋์ ์ค ์ผ๋ถ ํจํค์ง๋ฅผ ์ ์ธํ ์ ์๋ค. ์ ์ธ ๋์ ํ์ผ๋ค์ ์ (.)์ผ๋ก ๊ตฌ๋ถ๋ ์๋ฐ ํด๋์ค์ ํจํค์ง ์ด๋ฆ์ ์ฌ์ฉํ๋ค.
jacocoTestCoverageVerification {
violationRules {
rule {
// ...
excludes = [
"**.common.*",
"**.config.*",
'**.infra.**',
'**.*Application*',
'**.*DetailService*',
]
}
}
}
๋ณด๊ณ ์๊ฐ ์์ฑ๋๋ ์์น๋ฅผ ๋ฐ๋ก ์ค์ ํด์ฃผ์ง ์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ ๊ฒฝ๋ก์ธ build/reports/jacoco/test/html/index.html์ ์ ์ฅ๋๋ฉฐ ์์ฑ๋ html ๋ณด๊ณ ์๋ ๊ฐ ์ปค๋ฒ๋ฆฌ์ง ํญ๋ชฉ๋ง๋ค ์ด ๊ฐ์์ ๋์น ๊ฐ์๋ฅผ ํ์ํด ์ค๋ค.
Element๋ฅผ ํ๊ณ ๋ค์ด๊ฐ๋ฉด ์ด๋ค ๋ฉ์๋๊ฐ ํ ์คํธ๋์ง ์์๋์ง๋ ํ์ธํ ์ ์๋ค.
์ ์ธ ํด๋์ค ์๋ํ
ํ๋ก์ ํธ ๋ฃจํธ ๋๋ ํ ๋ฆฌ ํ์์ coverage-exclude.asap ํ์ผ์ ์์ฑํ๊ณ JaCoCo์์ ์ ์ธํ ํ์ผ์ด๋ ํจํค์ง๋ฅผ ์ ๋ ฅํ๋ค.
exclude com/chaewsstore/core/infra/*
exclude com/chaewsstore/*/common/*
exclude com/chaewsstore/*/common/*/*
exclude com/chaewsstore/*/config/*
exclude com/chaewsstore/*/config/*/*
exclude com/chaewsstore/*/*Application*
exclude com/chaewsstore/*/apis/*/service/*DetailService*
jacoco.gradle์ coverage-exclude.asap ํ์ผ์ ๋ด์ฉ์ excludeFromCoverage ๋ฆฌ์คํธ์ ์ ์ฅํ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ค.
import java.util.stream.Collectors
def excludeFromCoverage = new ArrayList<String>()
file('coverage-exclude.asap').withInputStream() { it ->
excludeFromCoverage.addAll(new BufferedReader(new InputStreamReader(it))
.lines()
.parallel()
.map(s -> s.substring(7).strip())
.collect(Collectors.toList()))
}
jacocoTestReport์ jacocoTestCoverageVerification์ excludes ๋ถ๋ถ์ ์๋์ ๊ฐ์ด ์์ ํ๋ค.
jacocoTestReport {
reports { ... }
afterEvaluate {
classDirectories.setFrom(
files(classDirectories.files.collect {
fileTree(dir: it, excludes: excludeFromCoverage.stream()
.map(s -> s + ".class")
.collect(Collectors.toList()))
})
)
}
finalizedBy 'jacocoTestCoverageVerification'
}
jacocoTestCoverageVerification {
violationRules {
rule {
...
excludes += excludeFromCoverage.stream()
.map(s -> s.replace("/", "."))
.collect(Collectors.toList())
}
}
}
JaCoCo ์ค์ ์ ์ฒด ์ฝ๋
import java.util.stream.Collectors
def excludeFromCoverage = new ArrayList<String>()
file('coverage-exclude.asap').withInputStream() { it ->
excludeFromCoverage.addAll(new BufferedReader(new InputStreamReader(it))
.lines()
.parallel()
.map(s -> s.substring(7).strip())
.collect(Collectors.toList()))
}
subprojects {
apply plugin: 'jacoco'
jacoco {
toolVersion = '0.8.12'
}
test {
useJUnitPlatform()
finalizedBy 'jacocoTestReport'
}
jacocoTestReport {
reports {
html.required.set(true)
xml.required.set(false)
csv.required.set(false)
}
afterEvaluate {
classDirectories.setFrom(
files(classDirectories.files.collect {
fileTree(dir: it, excludes: Qdomains + excludeFromCoverage.stream()
.map(s -> s + ".class")
.collect(Collectors.toList()))
})
)
}
finalizedBy 'jacocoTestCoverageVerification'
}
jacocoTestCoverageVerification {
violationRules {
rule {
enabled = true
element = "CLASS"
limit {
counter = "LINE"
value = "COVEREDRATIO"
minimum = 0.80
}
excludes += excludeFromCoverage.stream()
.map(s -> s.replace("/", "."))
.collect(Collectors.toList())
}
}
}
}
๐ JaCoCo Report Aggregation
๋ฉํฐ ๋ชจ๋๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ๋, ๋ฃจํธ build.gradle์ allProjects ํน์ subProjects๋ก ๋ชจ๋ ๋ชจ๋์ JaCoCo ํ๋ฌ๊ทธ์ธ์ ์ ์ฉํ ๊ฒฝ์ฐ JaCoCo์ test code coverage reports๊ฐ ๋ชจ๋๋ณ๋ก ์์ฑ๋๋ค. ์ด๋ ๊ฒ ๋๋ฉด ํน์ ๋ชจ๋์ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ๋ณด๊ธฐ ์ํด์๋ ํด๋น ๋ชจ๋์ build๋ฅผ ์ด์ด๋ด์ผ ํ๋ค๋ ๋จ์ ์ด ์๋ค. ๋ฐ๋ผ์ ์ด๋ ๊ฒ ๋ถ๋ฆฌ๋ jacoco coverage reports ํ์ผ์ ํฉ์น๊ธฐ ์ํด์ jacoco-aggregation ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ ์ ์๋ค.
Report Aggregation ํ๋ฌ๊ทธ์ธ ์ ์ฉ
๋ฃจํธ ๋๋ ํ ๋ฆฌ ํ์์ support/jacoco ๋ชจ๋์ ๋ง๋ค์ด :support:jacoco ํ๋ก์ ํธ์์ JaCoCo ๋ฆฌํฌํธ ์ง๊ณ๋ฅผ ์ค์ ํ๋ค.
project(':support:jacoco') {
apply plugin: 'jacoco-report-aggregation'
}
โโโ chaewsstore-admin
โโโ chaewsstore-app
โโโ chaewsstore-core
โโโ global-utils
โโโ support
โ โโโ jacoco
โ
โโโ build.gradle
โโโ jacoco.gradle
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ฆฌํฌํธ ์ค์
JaCoCo ์ค์ ์ค jacocoTestReport์ afterEvaluate๋ฅผ ํตํด ๋ณด๊ณ ์์ ํ์๋๋ ํด๋์ค ์ค ์ํ๋ ํด๋์ค๋ฅผ ์ ์ธํ ์ ์์๋๋ฐ, jacoco-aggregation์ผ๋ก ํตํฉ๋ report์๋ ์ฌ์ ํ exclude ํ ํด๋์ค๋ค์ด ๋ณด์ด๋ ๋ฌธ์ ๊ฐ ์๊ธด๋ค.
์ด๋ jacoco.gradle ํ์ผ์์ ์๋ ์ค์ ์ ์ถ๊ฐํด jacoco-aggregation์์๋ ํด๋์ค๋ฅผ ์ ๊ฑฐํ ์ ์๋ค.
testCodeCoverageReport {
getClassDirectories().setFrom(files(allProjects
.collect {
it.fileTree(dir: "${it.buildDir}/classes/java/main", exclude:
excludeFromCoverage.stream()
.map(s -> s + ".class")
.collect(Collectors.toList()))
}
))
}
์ข ์์ฑ ์ถ๊ฐ
support/jacoco ๋ชจ๋์์ ๋ค๋ฅธ ํ์ ๋ชจ๋๋ค์ ์ข ์์ฑ์ผ๋ก ์ถ๊ฐํด jacoco-report-aggregation์ด ์ด ํ์ ๋ชจ๋๋ค์ ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง ๋ฆฌํฌํธ๋ฅผ ์์งํ๊ณ ์ง๊ณํ ์ ์๋ค.
dependencies {
implementation project(':chaewsstore-admin')
implementation project(':chaewsstore-app')
implementation project(':chaewsstore-core')
}
์ข ์์ฑ ์ถ๊ฐ ์๋ํ
์์์์ฒ๋ผ ์ข ์์ฑ์ ํ๋์ฝ๋ฉํ ๊ฒฝ์ฐ ์๋ก์ด ๋ชจ๋์ด ์์ฑ๋ ๋๋ง๋ค jacoco-aggregation์ ์ถ๊ฐ๋ฅผ ํด์ฃผ์ด์ผ ํ๋ ๋จ์ ์ด ์๋ค. ๋ฐ๋ผ์ ์๋ก์ด ๋ชจ๋ ์์ฑ ์ ์๋์ผ๋ก ๊ฐ์งํด jacoco-aggregation ๋์์ผ๋ก ๋ฑ๋กํด ์ฃผ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํด ์ฃผ์๋ค.
def allProjects = getAllprojects().stream()
.filter(p -> !p.getDisplayName().contains('root project'))
.collect(Collectors.toList())
project(':support:jacoco') {
apply plugin: 'jacoco-report-aggregation'
testCodeCoverageReport { ... }
def allProjectsExcludeJacoco = allProjects.stream()
.filter(p -> !p.getDisplayName().contains('jacoco')
&& !p.getDisplayName().contains('root project'))
.collect(Collectors.toList())
dependencies {
allProjectsExcludeJacoco.each { project ->
implementation project
}
}
}
JaCoCo Aggregation ์ค์ ์ ์ฒด ์ฝ๋
def allProjects = getAllprojects().stream()
.filter(p -> !p.getDisplayName().contains('root project'))
.collect(Collectors.toList())
project(':support:jacoco') {
apply plugin: 'jacoco-report-aggregation'
testCodeCoverageReport {
getClassDirectories().setFrom(files(allProjects
.collect {
it.fileTree(dir: "${it.buildDir}/classes/java/main", exclude:
excludeFromCoverage.stream()
.map(s -> s + ".class")
.collect(Collectors.toList()))
}
))
}
def allProjectsExcludeJacoco = allProjects.stream()
.filter(p -> !p.getDisplayName().contains('jacoco')
&& !p.getDisplayName().contains('root project'))
.collect(Collectors.toList())
dependencies {
allProjectsExcludeJacoco.each { project ->
implementation project
}
}
}
๐ ์ถ๊ฐ์ ์ธ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ถ์ ๋์ ์ ์ธ
QClass
JPQ์ QueryDSL์ ์ฌ์ฉํ๋ฉด Q domain์ด ์๊ธฐ๋๋ฐ ํด๋น ๋ถ๋ถ์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ถ์ ๋์์์ ์ ์ธํด์ผ ํ๋ค.
jacoco.gradle์ ์๋ ์ฝ๋๋ฅผ ์ถ๊ฐํ๊ณ , excludes์ Qdomains๋ฅผ ์ถ๊ฐํด ์ค๋ค.
def Qdomains = []
for(qPattern in "**/QA" .. "**/QZ"){
Qdomains.add(qPattern+"*")
}
Generated ์ด๋ ธํ ์ด์ ์ถ๊ฐ
lombok ๊ด๋ จ ๋ฉ์๋
lombok์ผ๋ก ์๋ ์์ฑ๋๋ Getter, NoArgsConstructor, Builder ๋ฑ์ ๋ฉ์๋๋ ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง์ ์ธก์ ๋์ด ๋น๋์ ์คํจํ ์ ์๋ค. ๋ฐ๋ผ์ lombok ๊ด๋ จ ๋ฉ์๋๋ค์ ์ธก์ ์์ ์ ์ธํ๋ค.
ํ๋ก์ ํธ ๋ฃจํธ ๋๋ ํ ๋ฆฌ ํ์์ lombok.config ํ์ผ์ ์์ฑํ๊ณ ์๋์ ์ค์ ์ ํด์ฃผ๋ฉด ๋๋ค.
lombok.addLombokGeneratedAnnotation = true
์ด ์ค์ ์ ๋ชจ๋ lombok์ผ๋ก ์์ฑ๋ ๋ฉ์๋์ ๋ํด 'Generated' ์ด๋ ธํ ์ด์ ์ ๋ถ์ธ๋ค๋ ์ค์ ์ด๋ค.
์ด์ธ ๋ฉ์๋
ํ๋ก์ ํธ์์ ์ฌ์ ์ํ equals์ hasoCode๋ ํ ์คํธํ ํ์๊ฐ ์์ด ํ ์คํธ ์ผ์ด์ค์ ์ถ๊ฐํ์ง ์์๋๋ฐ, ์ด๋ ๋ง์ฐฌ๊ฐ์ง๋ก ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง์ ์ธก์ ๋์ด ๋น๋๊ฐ ์คํจํ ์ ์๊ฒ ๋๋ค. ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ํด ๋ถํ์ํ๊ฒ ํด๋น ๋ฉ์๋๋ฅผ ํ ์คํธํ๋ ๊ฒ๋ณด๋ค ๋ฉ์๋๋ฅผ ์ธก์ ์์ ์ ์ธํ๋ ๊ฒ์ด ๋ซ๋ค๊ณ ์๊ฐํ๋ค.
JaCoCo ์ค์ ์์ exclude ๊ฐ๋ฅํ ํญ๋ชฉ์ element์์ ์ ์๋ CLASS์ด๋ค. ํ์ง๋ง JaCoCo 0.8.2๋ถํฐ๋
- 'Generated'๋ผ๋ ๋จ์ด๊ฐ ์ด๋ฆ์ ํฌํจ
- RetentionPolicy๊ฐ 'CLASS' ๋๋ 'Runtime'
์ ์ถฉ์กฑํ๋ ์ด๋ ธํ ์ด์ ์ด ์์ผ๋ฉด ํด๋น ํ๊ฒ์ JaCoCo ์ธก์ ์์ ์ ์ธํ ์ ์๋ค๊ณ ํ๋ค. ๋ฐ๋ผ์ ์ ๊ธฐ์ค์ ์ถฉ์กฑํ๋ ์ด๋ ธํ ์ด์ ์ ๋ง๋ค๊ณ , ํ ์คํธ๊ฐ ๋ถํ์ํ ๋ฉ์๋์ ํด๋น ์ด๋ ธํ ์ด์ ์ ์ถ๊ฐํด ์ฃผ์๋ค.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Generated {
}
@Override
@Generated
public final boolean equals(Object o) {
...
}
@Override
@Generated
public final int hashCode() {
...
}
์ด ๋ฐฉ๋ฒ์ ํ์ํ ํ ์คํธ ์ผ์ด์ค๋ง ์ปค๋ฒ๋ฆฌ์ง ์ธก์ ์ ํจ์ผ๋ก์จ ์ ํํ ์ปค๋ฒ๋ฆฌ์ง ๋ถ์์ ํ ์ ์๊ฒ ํด ์ฃผ์ง๋ง ์๋น์ค ์ฝ๋์ ํ ์คํธ ์ฝ๋ ๋ก์ง์ด ๋ค์ด๊ฐ๋ค๋ ๋จ์ ์ด ์๋ค.
๐ค Generated ์ด๋ ธํ ์ด์ ์ด ๋ถ์ผ๋ฉด ์ report์์ ์ ์ธ๋ ๊น?
lombok ๊ด๋ จ ๋ฉ์๋์ ํ ์คํธ๊ฐ ๋ถํ์ํ ๋ฉ์๋์ @Generated ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ ์ธก์ ์์ ์ ์ธํ ์ ์๋ ์ด์ ๋ lombok์ ๋ฉ์๋ ์์ฑ ์์ ๊ณผ JaCoCo์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ์ธก์ ๋ฐฉ์์ ์๋ค.
๐ซง lombok ๋ฉ์๋ ์์ฑ ์์
lombok ๋ฉ์๋๋ ์ปดํ์ผ ์ ์์ฑ(annotation processing)๋๋ค. ์ฆ, ์์ค ์ฝ๋๊ฐ ์ปดํ์ผ๋ ๋ lombok์ด ์๋์ผ๋ก ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋๋ฐ, ์ด๋ ์ด ๋ฉ์๋์ @Generated ์ด๋ ธํ ์ด์ ์ ๋ถ์ธ๋ค.
๐ซง JaCoCo ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ์ธก์ ๋ฐฉ์
JaCoCo๋ Java Agent๋ก ์คํ๋์ด ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์ง์ ๊ด๋ฆฌํ๊ณ , ํ ์คํธ ์คํ ์ค ์ด๋ค ์ฝ๋ ๋ผ์ธ์ด ์ํ๋์๋์ง ํ์ธํ๋ฉฐ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ธก์ ํ๋ค. JaCoCo๋ ์คํ๋๋ ๋ฐ์ดํธ ์ฝ๋์ ๊ฐ ๋ผ์ธ์ ์ถ์ ํ์ฌ ์ปค๋ฒ๋ฆฌ์ง ๋น์จ์ ๊ณ์ฐํ๋๋ฐ, ์ด ๊ณผ์ ์์ @Generated๊ฐ ๋ถ์ ๋ฉ์๋๋ฅผ ์ธ์ํ๊ณ ์ด๋ฅผ ์ธก์ ๋์์์ ์ ์ธํ ์ ์๋ค.
๐ ์ฐธ๊ณ
์ฝ๋ ๋ถ์ ๋๊ตฌ ์ ์ฉ๊ธฐ - 2ํธ, JaCoCo ์ ์ฉํ๊ธฐ
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ํด ์ ์ฉ๊ธฐ(feat. JaCoCo)
[Jacoco] ๋ฉํฐ ๋ชจ๋์ Jacoco report ๋ฅผ ํ๋๋ก ํฉ์น๊ธฐ
'Back-end' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JUnit] ํ ์คํธ ์ฝ๋์ Test Fixture ์ฌ์ฉํ๊ธฐ: ํ ์คํธ ๋ฐ์ดํฐ ์ฌ์ฌ์ฉ (0) | 2024.08.05 |
---|---|
[Gradle] build.gradle ํ๋ฌ๊ทธ์ธ ๋ถ๋ฆฌํ๊ธฐ (0) | 2024.07.25 |
Jmeter๋ฅผ ์ฌ์ฉํ ๋์์ฑ ํ ์คํธ (0) | 2024.07.05 |
[Spring] Spring Security๋ฅผ ์ด์ฉํ JWT ๊ธฐ๋ฐ ์ธ์ฆ ๋ฐ ์ธ๊ฐ ์ค์ (0) | 2024.06.22 |
[Spring Boot] ๋ด๊ฐ ์ ์ฉํ!!! ๋ฉํฐ ๋ชจ๋ ๊ธฐ์ค (0) | 2024.06.12 |