[์ฝ๋๋ถ์๋๊ตฌ]#2 Jacoco ์ ์ฉํ๊ธฐ
by JiwonDev์ธ๋ค์ผ์ ๋ฑํ ๋ฃ์๊ฒ ์์ด์ ์ ํจ์ค๋ฅผ ๋ฃ์๋ค. ๊ทธ๋ฅ CI ๋๊ตฌ๋ผ๋ ๊ด์ ์์..?
JaCoCo is a free code coverage library for Java, which has been created by the EclEmma team based on the lessons learned from using and integration existing libraries for many years
- JaCoCo
Jacoco๋ Java Code์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ฒดํฌํ๋๋ฐ ์ฌ์ฉ๋๋ ์คํ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
# Jacoco์ ํน์ง
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง์ ์ต์ํ์ง ์์ ๋๋ ์ปค๋ฒ๋ฆฌ์ง ๊ธฐ์ค์ ๋ฎ์ถฐ์ ์ฌ์ฉํ๋ค๊ฐ ์ ์ ๊ธฐ์ค์ ๋์ด๋ ๊ฒ์ ์ถ์ฒํ๋ค. Jacoco๋ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ํ๊ฐํ๊ณ ํ๋ก์ ํธ ๋น๋ ์ฑ๊ณต/์คํจ๋ฅผ ๊ฒฐ์ ํ ์ ์๊ฒ ๋ง๋ค์ด์ค๋ค.
- ๋ผ์ธ ์ปค๋ฒ๋ฆฌ์ง(๋ชจ๋ ์ฝ๋๊ฐ ์คํ๋๋๊ฐ)์ ๋ธ๋ ์น ์ปค๋ฒ๋ฆฌ์ง(๋ชจ๋ ์กฐ๊ฑด๋ถ๊ธฐ๋ฅผ ์ฌ์ฉํ๋๊ฐ)๋ฅผ ์ ๊ณต.
- ํ ์คํธ์ฝ๋๋ฅผ ๋๋ฆฌ๊ณ ์ปค๋ฒ๋ฆฌ์ง ๊ธฐ์ค์ ๋ง์กฑํ๋์ง ํ์ธํ ์ ์๋ค.
- ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๊ฒฐ๊ณผ๋ฅผ ์ ๋ฆฌ๋ ํ์ผ(html, xml, csv๋ฑ)์ผ๋ก ์ ์ฅํ ์ ์๋ค.
Jacoco๋ฅผ ํตํด ์ฝ๋์ปค๋ฒ๋ฆฌ์ง ๊ฒฐ๊ณผ๋ฅผ csv, xml๋ก ์ ์ฅํ๋ฉด ์ด๋ฅผ ์๋ํ๋ธ(SonarQube) ๊ฐ์ ๋๊ตฌ์ ์ ๋ฌํด ๊ฒฐ๊ณผ์ ๋ฐ๋ผ ์ปค๋ฐ์ฑ๊ณต/์คํจ๋ฅผ ๊ฒฐ์ ํ๋ ์์ผ๋ก ์ฌ์ฉ๋๋ค. ์ฐธ๊ณ ๋ก html์ ์ฌ์ฉํ๋ฉด ์ด๋ ๋ถ๋ถ์ด ๋ฌธ์ ์ธ์ง ๊ฐ๋ฐ์๊ฐ ํ๋์ ํ์ ํ๊ธฐ ์ฝ๊ฒ ๋ณด๊ณ ์๋ก ๋ง๋ค์ด์ค๋ค.
# Jacoco ์ ์ฉํ๊ธฐ
Java + SpringBoot + Gradle๋ก ์ด๋ฃจ์ด์ง ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด์. ์คํ๋ง๋ถํธ๋ฅผ ์ฌ์ฉํ๋ฉด build.gradle ์ค์ ๋ง์ ์ด์ฉํด์ ์ฝ๊ฒ ์ ์ฉ๊ฐ๋ฅํ๋ค.
1. Jacoco๋ฅผ ์ ์ฒด ํ๋ก์ ํธ๋ฅผ ํ๋๋ก ์ผ๊ด์ ์ฉํ๊ณ ์ถ๋ค๋ฉด ์๋์ ๊ฐ์ดํ๋ฉด ๋๋ค.
//build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '2.3.1.RELEASE'
...
id 'jacoco' // ์ถ๊ฐ!
}
jacoco {
// JaCoCo ๋ฒ์
toolVersion = '0.8.6'
// ํ
์คํธ๊ฒฐ๊ณผ ๋ฆฌํฌํธ๋ฅผ ์ ์ฅํ ๊ฒฝ๋ก
// ๋ณ๊ฒฝํ๊ณ ์ ํ๋ฉด ์๋์ ๊ฐ์ด ์์ฑ default๋ "${project.reporting.baseDir}/jacoco"
reportsDir = file("$buildDir/customJacocoReportDir")
}
2. ๊ทธ๊ฒ ์๋๋ผ ๊ฐ ๋ชจ๋๋ณ๋ก ๋ฐ๋ก Jacoco๋ฅผ ์ ์ฉํ๊ณ ์ถ๋ค๋ฉด, ์๋์ ๊ฐ์ด ์ค์ ํด์ฃผ์.
// build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '2.3.1.RELEASE'
...
}
allprojects {...}
subprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'org.asciidoctor.convert'
apply plugin: 'jacoco'// ์ฌ๊ธฐ์ ์ถ๊ฐ!
...
}
jacoco {...}
๊ทธ๋ฌ๋ฉด ์๋์ ๊ฐ์ด ๊ฐ ๋ชจ๋์ Gradle ๋๋ ํ ๋ฆฌ์ Jacoco ์์กด์ฑ์ด ์ถ๊ฐ๋๋ค.
- jacocoTestReport : ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์งํํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ฌ๋์ด ์ฝ๊ธฐ ์ข์ ๋ฆฌํฌํธ๋ก ์ ์ฅํด์ฃผ๋ ๋๊ตฌ
- jacocoTestCoverageVerification : ์ํ๋ ์ปค๋ฒ๋ฆฌ์ง ๊ธฐ์ค์ ๋ง์กฑํ๋์ง ํ์ธํด์ฃผ๋ ๊ฒ์ฆ ๋๊ตฌ
์ด ์ค์ ์ build.gradle์์ ํ ์ ์๋๋ฐ, ํ๊ฐ์ง ์ฃผ์ํ ์ ์ jacoco๋ ํ ์คํธ ์ดํ ์ ์ฉ๋๋ ํ๋ฌ๊ทธ์ธ์ด๋ผ๋ ์ ์ด๋ค. ์ฆ [ Junit Test -> jacocoTestReport -> jacoco...CoverageVerification ] ์์ผ๋ก ์คํ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ์๋์ ๊ฐ์ด ํ ์คํธ ์ค์ ์ ๊ณ ์ ์์ผ์ฃผ์. ์ด๋ฅผ ์๋ตํ๊ณ ์ํด์ฃผ๋ฉด ์ด์ ํ ์คํธ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๋ณตํด์ ์ฝ์ด ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ๊ณ์ ํต๊ณผ ๋ชปํ๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ ์ ์๋ค.
//build.gradle
test {
useJUnitPlatform() // JUnit5๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ์ค์
finalizedBy 'jacocoTestReport' // Test ์ดํ ์ปค๋ฒ๋ฆฌ์ง๊ฐ ๋์ํ๋๋ก finalizedBy ์ถ๊ฐ
}
jacoco {
toolVersion = '0.8.6'
}
jacocoTestReport {
reports {
html.enabled true
csv.enabled true
xml.enabled false
}
finalizedBy 'jacocoTestCoverageVerification' // ์ปค๋ฒ๋ฆฌ์ง ์๋ ์ดํ ๊ฒ์ฆํ๋๋ก ์ค์
}
# jacocoTestReport (์ปค๋ฒ๋ฆฌ์ง ์ธก์ )
build.gradle์์ jacocoTestReport {...}์ ์ค์ ๊ฐ๋ฅํ๋ค. ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๊ฒฐ๊ณผ๋ฅผ ์ด๋ป๊ฒ ์ ์ฅํ ์ง ์ค์ ํ๋ค.
//build.gradle
...
jacoco {
toolVersion = '0.8.6'
}
jacocoTestReport {
reports {
csv.enabled true // csv ์ค์
xml.enabled false // xml ๋ฏธ์ค์
html.enabled true // html ์ค์
// ํ์ผ ํ์์ ๋ฐ๋ผ ์ ์ฅ๊ฒฝ๋ก๋ฅผ ๋ฐ๋ก ์ง์ ํด์ค์๋ ์๋ค.
html.destination file('build/reports/myReport.html')
...
}
}
๋ฆฌํฌํธ๋ฅผ ์์ฑํ ๋ ํน์ ํ์ผ์ ์ ์ธํ๊ณ ์ถ๋ค๋ฉด, ์๋์ ๊ฐ์ด ์์ฑํ๋ฉด ๋๋ค.
jacocoTestReport {
reports {
html.enabled true
csv.enabled true
xml.enabled false
}
def Qdomains = []
for (qPattern in '**/QA'..'**/QZ') { // qPattern = '**/QA', '**/QB', ... '*.QZ'
Qdomains.add(qPattern + '*') // ~/QA* , QA๋ก ์์ํ๋ ๋ชจ๋ ํด๋์ค
}
// ์ฌ๊ธฐ๋ถํฐ
afterEvaluate {
classDirectories.setFrom(
files(classDirectories.files.collect {
fileTree(dir: it, excludes: [] + Qdomains)
})
)
}
finalizedBy 'jacocoTestCoverageVerification'
}
- afterEvaluate๋ gradle์ ๋น๋ ๋ผ์ดํ ์ฌ์ดํด์ ๋ํ ๋ฉ์๋. ํ๋ก์ ํธ๊ฐ ํ๊ฐ๋ ํ ์คํํ ์ ์๋๋ก ๋์์ค๋ค.
- classDirectories๋ ์ปค๋ฒ๋ฆฌ์ง๊ฐ ๋ฆฌํฌํธ๋ก ์์ฑํ ์์ค ํ์ผ์ ๋งํ๋ค. (์ฌ๊ธฐ์๋ setFrom ๋ฉ์๋๋ฅผ ํตํด ์ด๋ฅผ ์ค์ )
- files๋ ์ง์ ๋ ํ์ผ์ ํฌํจํ๋ ConfigurableFileCollection ํ์ ์ ๋ฐํ.
์ด๋ฐ์์ผ๋ก ๋ฉ์๋๋ฅผ ํตํด ๋ฆฌํฌํธ์ ์ ์ธํ excludes ๋ชฉ๋ก์ ์ถ๊ฐ๋ก ์ง์ ํด์ค ์ ์๋ค.
# jacocoTestCoverageVerification ( ์ ์ํ๊ฐ, ๊ฒ์ฆ )
์ต์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ค์ ํ ์ ์๊ณ , ํต๊ณผํ์ง ๋ชปํ ๊ฒฝ์ฐ ํด๋น Task๊ฐ ์คํจํ๊ฒ ๋๋ค.
jacoco...verification { violationRules{~} } ๋ฅผ ํตํด ์ปค๋ฒ๋ฆฌ์ง ๊ธฐ์ค ๊ท์น์ ์ ํ ์ ์๋ค.
- rule { ... } ๊ท์น์ ์ ๋ ๊ณต๊ฐ. ์ฌ๋ฌ๊ฐ๋ฅผ ์์ฑํ ์ ์๊ณ enable ์ต์ ์ผ๋ก on/off ํ ์ ์๋ค.
- element = ์ ์ฉํ ํ์
๋๋ ๋์์ ์ง์ ํ๋ค (์ ์ฒด, ํด๋์ค, ๋ฉ์๋, ํ ํ์ผ ๋ฑ๋ฑ..)
- BUNDLE(๊ธฐ๋ณธ๊ฐ) : ํจํค์ง ๋ฒ๋ค(ํ๋ก์ ํธ ๋ชจ๋ ํ์ผ์ ํฉ์น ๊ฒ)
- CLASS : ํด๋์ค
- GROUP : ๋ ผ๋ฆฌ์ ๋ฒ๋ค ๊ทธ๋ฃน
- METHOD : ๋ฉ์๋
- PACKAGE : ํจํค์ง
- SOURCEFILE : ์์ค ํ์ผ
- includes, exclude = ์์ฑํ๋ฉด ์ ์ธ ๋์/์ ์ฉ ๋์์ ํด๋์ค๋ ํจํค์ง ๊ฒฝ๋ก๋ก ํ์ ํ ์ ์๋ค.
- ์ ๊ทํํ์์ธ * ์ ? ๋ฅผ ์ฌ์ฉํ ์ ์๋ค. import ํ๋ฏ์ด ๋ฆฌ์คํธ๋ฅผ ์ ์ผ๋ฉด ๋๋ค.
jacocoTestCoverageVerification {
violationRules {
rule { // 1๋ฒ ๊ท์น
enable = true // ๊ท์น ํ์ฑํ์ฌ๋ถ
element = 'CLASS' // ์ ์ฉ ๋์์ ์ง์ ํ๋ค. ๊ธฐ๋ณธ๊ฐ์ BUNDLE (์ ์ฒด ํจํค์ง)
// BUNDLE, CLASS, GROUP(๋
ผ๋ฆฌ์ ๋ฒ๋ค๊ทธ๋ฃน), METHOD, PACKAGE, SOURCEFILE ๋ฑ
// ํด๋น ๊ท์น์ ์ ์ฉ๋์์ ํด๋์ค, ํจํค์ง ๋จ์๋ก ์ง์ ๊ฐ๋ฅ
// includes = [], ๋จ include๋ ์๋ตํ๋ฉด ์ ์ฒด ํจํค์ง์ ์ผ๊ด ์ ์ฉ
// excludes = [], ์ ์ธํ ํจํค์ง/ํด๋์ค๋ฅผ ์ง์
limit {
counter = 'BRANCH' // ์ปค๋ฒ๋ฆฌ์ง ์ข
๋ฅ
value = 'COVEREDRATIO' // ๊ฐ (์ ์ฒด ๋น์จ)
minimum = 0.90 // ์ต์ ๊ธฐ์ค 90%
}
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.80 // ์ต์ ๊ธฐ์ค 80%
}
limit {
counter = 'LINE'
value = 'TOTALCOUNT' // ๊ฐ(์ ์ฒด ๊ฐ์)
maximum = 200 // ์ต์ ๊ธฐ์ค 200์ค ์ดํ
}
}
// ์ฌ๋ฌ rule์ ์์ฑํ ์ ์์ต๋๋ค.
rule { // 2๋ฒ ๊ท์น
...
}
}
}
- limit = ์ปค๋ฒ๋ฆฌ์ง ์ธก์ ์ต์๋จ์๋ฅผ ์ง์ ํ๋ค.
limit {
counter = 'BRANCH' // ์ปค๋ฒ๋ฆฌ์ง ์ข
๋ฅ
value = 'COVEREDRATIO' // ๊ฐ (์ ์ฒด ๋น์จ)
minimum = 0.90 // ์ต์ ๊ธฐ์ค 90%
}
Counter
- BRANCH : ์กฐ๊ฑด๋ฌธ ๋ฑ์ ๋ถ๊ธฐ ์
- CLASS : ํด๋์ค ์, ๋ด๋ถ ๋ฉ์๋๊ฐ ํ ๋ฒ์ด๋ผ๋ ์คํ๋๋ค๋ฉด ์คํ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ค.
- COMPLEXITY : ์ฝ๋ ๋ณต์ก๋(๋งํฌ ์ฐธ์กฐ)
- INSTRUCTION(๊ธฐ๋ณธ๊ฐ) : Java ๋ฐ์ดํธ์ฝ๋ ๋ช ๋ น ์
- METHOD : ๋ฉ์๋ ์, ๋ฉ์๋๊ฐ ํ ๋ฒ์ด๋ผ๋ ์คํ๋๋ค๋ฉด ์คํ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ค.
- LINE : ๋น ์ค์ ์ ์ธํ ์ค์ ์ฝ๋์ ๋ผ์ธ ์, ๋ผ์ธ์ด ํ ๋ฒ์ด๋ผ๋ ์คํ๋๋ฉด ์คํ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ค.
Value
- COVEREDCOUNT : ์ปค๋ฒ๋ ๊ฐ์
- COVEREDRATIO(๊ธฐ๋ณธ๊ฐ) : ์ปค๋ฒ๋ ๋น์จ, 0๋ถํฐ 1์ฌ์ด์ ์ซ์๋ก [1 = 100%]์ด๋ค.
- MISSEDCOUNT : ์ปค๋ฒ๋์ง ์์ ๊ฐ์
- MISSEDRATIO : ์ปค๋ฒ๋์ง ์์ ๋น์จ, 0๋ถํฐ 1์ฌ์ด์ ์ซ์๋ก [1 = 100%]์ด๋ค.
- TOTALCOUNT : ์ ์ฒด ๊ฐ์
Minimum
- ์์ Value๋ก ์ง์ ํ ํ์ ์ ์ต์๊ธฐ์ค๊ฐ์ ์ ๋ ฅํ๋ฉด ๋๋ค. jacocoTest์ ์ฑ๊ณต์ฌ๋ถ๊ฐ ๊ฒฐ์ ๋๋ ๊ธฐ์ค๊ฐ์ด๋ฉฐ ํ๊ธฐํ ์๋ฆฟ ์๋งํผ value๊ฐ ์ธ๋ฐํ๊ฒ ์ธก์ ๋๋ค. ์ฆ 0.80์ ์ ๋ ฅํ๋ฉด ์์ซ์ ๋์งธ์๋ฆฌ๊น์ง, 0.8์ ์ ๋ ฅํ๋ฉด ์ฒซ์งธ ์๋ฆฌ๊น์ง๋ง ์ธก์ ํ๋ฉฐ ๊ทธ ์ดํ์ ๊ฐ๋ค์ ๋ฒ๋ ค์ง๋ค. (๋ฐ์ฌ๋ฆผ ์๋)
# Test ์ค์ ํ๊ธฐ
๋ชจ๋ ํ ์คํธ ํ์ ์ JacocoTaskExtension์ ์ถ๊ฐํ๊ณ , test {...}์์ ์ค์ ์ ๋ณ๊ฒฝํ ์ ์๋ค.
์ด๋ ๊ฒ ์ค์ ํ Jacoco ์ปค๋ฒ๋ฆฌ์ง๋ ๊ธฐ์ค์ ๋ง์กฑํ์ง๋ชปํ๋ฉด ํ๋ก์ ํธ๊ฐ gradle์ ๋น๋๋์ง ์๋๋ค.
//build.gradle
test { // ๊ฐ ์ค์ ๊ฐ์ ์ค๋ฒ๋ผ์ด๋๊ฐ ๊ฐ๋ฅํ๋ค.
jacoco {
enabled = true
destinationFile = file("$buildDir/jacoco/${name}.exec")
includes = []
excludes = []
excludeClassLoaders = []
includeNoLocationClasses = false
sessionId = "<auto-generated value>"
dumpOnExit = true
classDumpDir = null
output = JacocoTaskExtension.Output.FILE
address = "localhost"
port = 6300
jmx = false
}
}
๋ง์ฝ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง์ ์ ์ธํ๊ณ ์ถ์ ์ด๋ฆ๊ท์น์ด ์๋ค๋ฉด, ๊ทธ๋ฃจ๋น ๋ฌธ๋ฒ์ ์ฌ์ฉํด์ exclude= [...]์ ์ ์ฉ์์ผ์ฃผ์.
jacocoTestCoverageVerification {
def Qdomains = []
// ์ง์ ํ๋ํ๋ ์ง์ ํด์ค๋ ๋๊ณ , ์ด๋ฐ์์ผ๋ก ํจํด์ ์ด์ฉํด๋ ๋๋ค.
for (qPattern in '*.QA'..'*.QZ') { // qPattern = '*.QA', '*.QB', ... '*.QZ'
Qdomains.add(qPattern + '*')
}
violationRules {
rule {
...
excludes = [] + Qdomains // ์ ์ธํ Qdomains ํจํด ์ถ๊ฐ
}
}
}
}
# lombok์ผ๋ก ์์ฑ๋ ์ฝ๋ ์ ์ธํ๊ธฐ
๊ทธ๋ฅ lombok ์ค์ ํ๋ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค. ๊ฐ๋จ
//build.gradle
lombok.config{
lombok.addLombokGeneratedAnnotation = true
}
# ์ ์ฒด ์ค์ ํ๋์ ๋ณด๊ธฐ
์ฐธ๊ณ ๋ก ๋ชจ๋์ ์ฌ๋ฌ๊ฐ ์ฌ์ฉํ ๋, Jacoco๊ฐ ์คํ๋์ค ์ปค๋ฒ๋ฆฌ์ง ๊ธฐ์ค๋ฏธ๋ฌ์ธ ๋ชจ๋์ ๋ฐ๊ฒฌํ๋ฉด ๋ฐ๋ก ์คํ์ ๋ฉ์ถ๋ค. ๊ทธ๋์ report๊ฐ ์์ฑ๋์ง ์๋ ๊ฒฝ์ฐ๋ ์๋๋ฐ ๋ง์ฝ ์คํจ์ฌ๋ถ์ ์๊ด์์ด ๊ฐ๋ณ์ ์ผ๋ก ์ ๋ถ ๋๋ ค๋ณด๊ณ ์ถ๋ค๋ฉด /gradlew test์ --continue๋ฅผ ์ถ๊ฐํ๋ฉด ๋๋ค.
/*...*/
subprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'org.asciidoctor.convert'
apply plugin: 'jacoco' // ๋ฉํฐ ๋ชจ๋ ์ ์ฉ
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
test {
useJUnitPlatform()
finalizedBy 'jacocoTestReport' // test ์ดํ ์ปค๋ฒ๋ฆฌ์ง ์คํ
}
jacoco {
toolVersion = '0.8.6'
}
jacocoTestReport {
reports {
html.enabled true
csv.enabled true
xml.enabled false
}
def Qdomains = []
for (qPattern in '**/QA'..'**/QZ') { // qPattern = '*.QA', '*.QB', ... '*.QZ'
Qdomains.add(qPattern + '*')
}
afterEvaluate { // report ์ ์ธ๋์ ์ง์
classDirectories.setFrom(
files(classDirectories.files.collect {
fileTree(dir: it, excludes: [] + Qdomains)
})
)
}
finalizedBy 'jacocoTestCoverageVerification' // ์ปค๋ฒ๋ฆฌ์ง ์ดํ ํ๊ฐ
}
jacocoTestCoverageVerification { // ์ปค๋ฒ๋ฆฌ์ง ๊ธฐ์ค๊ฐ ์์ธ์ค์
def Qdomains = []
for (qPattern in '*.QA'..'*.QZ') { // qPattern = '*.QA', '*.QB', ... '*.QZ'
Qdomains.add(qPattern + '*')
}
violationRules {
rule {
enabled = true
element = 'CLASS'
limit {
counter = 'LINE'
value = 'COVEREDRATIO'
minimum = 0.00
}
limit {
counter = 'BRANCH'
value = 'COVEREDRATIO'
minimum = 0.00
}
excludes = [] + Qdomains // ์ปค๋ฒ๋ฆฌ์ง ํ๊ฐ ์ ์ธ ๋์
}
}
}
}
/*...*/
๋ค์ ๊ธ์์๋ Jacoco์ ์ฝ๋์ปค๋ฒ๋ฆฌ์ง ๊ฒฐ๊ณผ๋ฅผ ์ด์ฉํด์ ์ ์ ๋ถ์ CI ๋๊ตฌ์ธ SonarQube์ ์ฐ๋ํด๋ณด๋๋ก ํ๊ฒ ๋ค. ์ ์ ๋ถ์๋๊ตฌ์ Jacoco์ ๋ค๋ฅธ์ ์, Jacoco๋ ๋จ์ํ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๊ฒฐ๊ณผ๋ฅผ ์ด์ฉํด์ ๋น๋ ์ฑ๊ณต/์คํจ์ฌ๋ถ๋ฅผ ์ธก์ ํ๋๊ฒ ๋์ด๋ผ๋ฉด SonarQube๋ ์ฝ๋ ํ์ง๊ฒ์ฌ๋ฅผ ํตํด ๋ฒ๊ทธ, ์ ๋ํ ์คํธ, ์ฃผ์, ๋ณต์ก๋, ํ์ค ์ปจ๋ฒค์ (์ฝ๋ Smell ์ธก์ )๋ฑ์ ๋ถ์ํด ์ทจ์ฝ์ ๊ณผ ๋ณด๊ณ ์๋ฅผ ์ ๊ณตํ๋ค.
'๐ฑBackend > Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
ํ ์คํธ ๋ชจํน Mockito ๋ผ์ด๋ธ๋ฌ๋ฆฌ (0) | 2021.09.07 |
---|---|
[์ฝ๋๋ถ์๋๊ตฌ]# 3 SonarLint, SonarQube (0) | 2021.08.21 |
[์ฝ๋๋ถ์ ๋๊ตฌ]# 1 ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋? (0) | 2021.08.21 |
#22 ์๋ฐ ์ฑ ๋ฐฐํฌ (JAR, WAR) (0) | 2021.07.21 |
#21 Servlet Context Listener (0) | 2021.07.21 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev