'JaCoCo skipping specific package with Quarkus
I have the following classes in separated files:
@Path("/products")
@Produces(MediaType.APPLICATION_JSON)
class ProductController{
@Inject
private ProductService productService;
}
@ApplicationScoped
public class ProductService {
@Inject
private ProductRepository productRepository;
}
@ApplicationScoped
public class ProductRepository implements PanacheRepositoryBase<Product, UUID> {
}
And when I run the tests coverage, all controllers and services and other packages are correctly recognized by JaCoCo, but all repositories aren't, resulting in 0% coverage in this package. And I'm sure that they are being used by services.
My guess is because JaCoCo only recognizes up to 1 level of "proxy", due to @AppicationScoped annotation, but I'm not sure.
I also tried offline intrumentation, but the result is the same.
This is my gradle config now:
plugins {
id 'java'
id 'io.quarkus'
id 'jacoco'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
// a lot of deps not related to tests
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
compileJava {
options.encoding = 'UTF-8'
options.compilerArgs << '-parameters'
}
compileTestJava {
options.encoding = 'UTF-8'
}
test {
useJUnitPlatform()
jacoco {
destinationFile = file("$buildDir/jacoco/test.exec")
}
finalizedBy jacocoTestReport
}
jacoco {
toolVersion = '0.8.6'
reportsDir = file("$buildDir/reports/jacoco")
}
jacocoTestCoverageVerification {
executionData fileTree("$buildDir/jacoco/").include("*.exec")
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it)
}))
}
}
jacocoTestReport {
executionData fileTree("$buildDir/jacoco/").include("*.exec")
reports {
xml.enabled false
csv.enabled false
html.destination file("${buildDir}/reports/jacoco")
}
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it)
}))
}
}
And this is the other "version" of gradle config, with offline instrumentation:
configurations {
jacocoAnt
jacocoRuntime
}
dependencies {
jacocoAnt group: 'org.jacoco', name: 'org.jacoco.ant', version: '0.8.5', classifier: 'nodeps'
jacocoRuntime group: 'org.jacoco', name: 'org.jacoco.agent', version: '0.8.5', classifier: 'runtime'
}
test {
useJUnitPlatform()
jacoco {
destinationFile = file("$buildDir/reports/jacoco/jacoco-sonar/jacoco-coverage.exec")
}
}
jacoco {
toolVersion = "0.8.5"
reportsDir = file("$buildDir/customJacocoReportDir")
}
jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.destination file("${buildDir}/jacocoHtml")
}
}
jacocoTestCoverageVerification {
executionData fileTree("$buildDir/reports/jacoco/jacoco-sonar/").include("*.exec")
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it)
}))
}
violationRules {
rule {
limit {
minimum = 0.74
}
}
}
}
task instrument(dependsOn: ['classes']) {
ext.outputDir = buildDir.path + '/reports/classes-instrumented'
doLast {
ant.taskdef(name: 'instrument',
classname: 'org.jacoco.ant.InstrumentTask',
classpath: configurations.jacocoAnt.asPath)
ant.instrument(destdir: outputDir) {
sourceSets.main.output.classesDirs.each { fileset(dir: it) }
}
}
}
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(instrument)) {
tasks.withType(Test) {
doFirst {
classpath = files(instrument.outputDir) + classpath + configurations.jacocoRuntime
}
}
}
}
task report(dependsOn: ['instrument', 'test']) {
doLast {
ant.taskdef(name: 'report',
classname: 'org.jacoco.ant.ReportTask',
classpath: configurations.jacocoAnt.asPath)
ant.report() {
executiondata {
ant.file(file: buildDir.path + '/reports/jacoco/jacoco-sonar/jacoco-coverage.exec')
}
structure(name: 'Example') {
classfiles {
sourceSets.main.output.classesDirs.each { fileset(dir: it) }
}
sourcefiles {
fileset(dir: 'src/main/java')
}
}
html(destdir: buildDir.path + '/reports/jacoco')
}
}
}
Why it happens?
Solution 1:[1]
Quarkus bytecode instrumentation and JaCoCo can step on each other toes, JaCoCo default mode uses an agent the inject some bytecode and this can be incompatible with Quarkus' own bytecode injection.
You can switch JaCoCo to offline instrumentation instead, please follow this section of the Quarkus test coverage guide: https://quarkus.io/guides/tests-with-coverage#instrumenting-the-classes-instead
Solution 2:[2]
I was facing the same problem as you, after include the following extension the coverage problem for @ApplicationScoped annotated classes was solved.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jacoco</artifactId>
<scope>test</scope>
</dependency>
Obviously, since you are using Gradle, you should change it. I also needed to remove jacoco-maven-plugin plugin from maven plugins section. Probably you will need to remove everything that you included for custom instrumentation.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | loicmathieu |
| Solution 2 |

