SupercovBlog

Guide

JaCoCo exclude in Maven and Gradle: packages, classes and generated code

The setups that work in Maven and Gradle, and five that look right but are not, measured with JaCoCo 0.8.16.

Excluded from the report. Still in the check. A brushed-aluminum tray of white tiles, with one violet tile lifted out of its pocket.

Write class-file paths, not package names: com/example/dto/**, not com.example.dto.*. Then give the same list to the report and to the coverage check. Most excludes that “don’t work” break one of those two rules.

Exclude patterns at a glance

To excludeWrite
A package and its subpackagescom/example/dto/**
A package name anywhere**/dto/**
A class and its nested classescom/example/Application*.class
Classes by name**/*Dto.class
Spring Boot’s main class**/*Application*.class
One methodAnnotate it @Generated
Lombok codeNothing, since Lombok 1.18.34

* matches within one folder, ** across folders. The patterns match files under target/classes or build/classes, so a nested class is its own file: Application$Args.class. **/*Application*.class also matches any other class with Application in its name.

We ran every setup here with JaCoCo 0.8.16, Maven 3.9.16 and Gradle 9.7.1 on JDK 26. The test project has five classes; two have tests. Without excludes, JaCoCo reports 8 of 25 lines (32%).

A diagram. prepare-agent instruments classes and writes jacoco.exec; both the report and the check read the class files again, and each needs the excludes. Excludes on the agent leave classes listed at 0%. With excludes on the report only, the report shows 80% and the check sees 32% and fails the build.
The report and the check each read the class files again. Give both the same list.

Exclude a package or class in Maven

Put <excludes> in the plugin’s own <configuration>, not inside one <execution>. Then report and check both use it.

<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.8.16</version>
  <configuration>
    <excludes>
      <exclude>com/example/dto/**</exclude>
      <exclude>com/example/Application*.class</exclude>
    </excludes>
  </configuration>
  <executions>
    <execution><goals><goal>prepare-agent</goal></goals></execution>
    <execution>
      <id>report</id><phase>test</phase><goals><goal>report</goal></goals>
    </execution>
  </executions>
</plugin>

Result: two classes left, 8 of 10 lines.

Exclude a package or class in Gradle

Gradle has no excludes setting for the report. Filter the class folders the task reads, on both jacocoTestReport and jacocoTestCoverageVerification.

Kotlin DSL, build.gradle.kts:

val coverageExcludes = listOf("com/example/dto/**", "com/example/Application*.class")

listOf(tasks.jacocoTestReport, tasks.jacocoTestCoverageVerification).forEach { task ->
    task {
        classDirectories.setFrom(
            files(classDirectories.files.map { fileTree(it) { exclude(coverageExcludes) } })
        )
    }
}

Groovy DSL, build.gradle:

def coverageExcludes = ['com/example/dto/**', 'com/example/Application*.class']

[jacocoTestReport, jacocoTestCoverageVerification].each { task ->
    task.classDirectories.setFrom(
        files(task.classDirectories.files.collect { fileTree(it) { exclude coverageExcludes } })
    )
}

Both gave the same result as Maven: two classes, 8 of 10 lines.

Why your JaCoCo exclude isn’t working

What we triedWhat happened
com.example.dto.* (dots)Nothing excluded
com/example/dto (no /**)Nothing excluded
com/example/Application.classIts nested class stayed, at 0 of 6 lines
Maven: excludes in the report execution only, check at 80%Report right, build failed: “lines covered ratio is 0.32”
Gradle: excludes on jacocoTestReport only, verification at 80%Same failure, same 0.32
Excludes on prepare-agent, or in Gradle’s test { jacoco { excludes } }Every class still listed
A grid of six setups against five classes. No excludes, dots, a missing /** and agent excludes keep all five classes, 8 of 25 lines. dto/** with Application*.class keeps Pricing and Receipt, 8 of 10. Application.class alone leaves its nested class and OrderDto in, 8 of 21.
What each setup left in the report, measured with JaCoCo 0.8.16 and Maven 3.9.16.

The last row catches many people. Those are the agent’s excludes. The classes are not instrumented, but the report still lists them, uncovered. Use them only for classes that fail under instrumentation.

Exclude generated code, Lombok and single methods

JaCoCo drops any class or method annotated with an annotation whose simple name contains Generated, as long as it survives compilation (CLASS or RUNTIME retention). A method we marked with our own @Generated vanished from the report. JaCoCo has no other way to exclude a single method.

javax.annotation.processing.Generated, which many code generators add, is discarded at compile time (SOURCE retention). A class carrying it stayed in the report. Exclude generated classes by path instead.

Lombok has marked everything it generates @lombok.Generated since 1.18.34. With Lombok 1.18.48, our @Data class was gone from the report with no lombok.config. On older Lombok, add lombok.addLombokGeneratedAnnotation = true to lombok.config.

Exclude the same files in SonarQube

SonarQube reads JaCoCo’s XML report, but counts files missing from it as uncovered. List the same files, as source paths, in sonar.coverage.exclusions. See JaCoCo vs SonarQube coverage.

What Supercov adds

npx supercov -- mvn test
npx supercov runs latest gaps

Supercov measures source files, not class files, so JaCoCo’s excludes and @Generated don’t apply to it. You name the directories that hold your own code. It records which test covered each line, with branches and MC/DC. On this project it showed that no test tried a large order from a non-member.

For tools beyond JaCoCo, see Java code coverage.