The JaCoCo Plugin
The JaCoCo plugin provides code coverage metrics for Java code via integration with JaCoCo.
Getting Started
To get started, apply the JaCoCo plugin to the project you want to calculate code coverage for.
plugins {
jacoco
}
plugins {
id 'jacoco'
}
If the Java plugin is also applied to your project, a new task named jacocoTestReport is created.
By default, an HTML report is generated at layout.buildDirectory.dir("reports/jacoco/test/html").
While tests should be executed before generation of the report, the jacocoTestReport task does not depend on the test task.
|
Depending on your usecases, you may want to always generate the jacocoTestReport or run the test task before generating the report explicitly.
tasks.test {
finalizedBy(tasks.jacocoTestReport) // report is always generated after tests run
}
tasks.jacocoTestReport {
dependsOn(tasks.test) // tests are required to run before generating the report
}
test {
finalizedBy jacocoTestReport // report is always generated after tests run
}
jacocoTestReport {
dependsOn test // tests are required to run before generating the report
}
Configuring the JaCoCo Plugin
The JaCoCo plugin adds a project extension named jacoco of type JacocoPluginExtension, which allows configuring defaults for JaCoCo usage in your build.
jacoco {
toolVersion = "0.8.15"
reportsDirectory = layout.buildDirectory.dir("customJacocoReportDir")
}
jacoco {
toolVersion = "0.8.15"
reportsDirectory = layout.buildDirectory.dir('customJacocoReportDir')
}
| Property | Gradle default |
|---|---|
toolVersion |
|
reportsDirectory |
|
JaCoCo Report configuration
The JacocoReport task can be used to generate code coverage reports in different formats. It implements the standard Gradle type Reporting and exposes a report container of type JacocoReportsContainer.
tasks.jacocoTestReport {
reports {
xml.required = false
csv.required = false
html.outputLocation = layout.buildDirectory.dir("jacocoHtml")
}
}
jacocoTestReport {
reports {
xml.required = false
csv.required = false
html.outputLocation = layout.buildDirectory.dir('jacocoHtml')
}
}
Enforcing code coverage metrics
| This feature requires the use of JaCoCo version 0.6.3 or higher. |
The JacocoCoverageVerification task can be used to verify if code coverage metrics are met based on configured rules. Its API exposes the method JacocoCoverageVerification.violationRules(org.gradle.api.Action) which is used as main entry point for configuring rules. Invoking any of those methods returns an instance of JacocoViolationRulesContainer providing extensive configuration options. The build fails if any of the configured rules are not met. JaCoCo only reports the first violated rule.
Code coverage requirements can be specified for a project as a whole, for individual files, and for particular JaCoCo-specific types of coverage, e.g., lines covered or branches covered. The following example describes the syntax.
tasks.jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = "0.5".toBigDecimal()
}
}
rule {
isEnabled = false
element = "CLASS"
includes = listOf("org.gradle.*")
limit {
counter = "LINE"
value = "TOTALCOUNT"
maximum = "0.3".toBigDecimal()
}
}
}
}
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.5
}
}
rule {
enabled = false
element = 'CLASS'
includes = ['org.gradle.*']
limit {
counter = 'LINE'
value = 'TOTALCOUNT'
maximum = 0.3
}
}
}
}
The JacocoCoverageVerification task is not a task dependency of the check task provided by the Java plugin. There is a good reason for it. The task is currently not incremental as it doesn’t declare any outputs. Any violation of the declared rules would automatically result in a failed build when executing the check task. This behavior might not be desirable for all users.
JaCoCo specific task configuration
The JaCoCo plugin adds a JacocoTaskExtension extension to all tasks of type Test. This extension allows the configuration of the JaCoCo specific properties of the test task.
tasks.test {
extensions.configure(JacocoTaskExtension::class) {
destinationFile = layout.buildDirectory.file("jacoco/jacocoTest.exec").get().asFile
classDumpDir = layout.buildDirectory.dir("jacoco/classpathdumps").get().asFile
}
}
test {
jacoco {
destinationFile = layout.buildDirectory.file('jacoco/jacocoTest.exec').get().asFile
classDumpDir = layout.buildDirectory.dir('jacoco/classpathdumps').get().asFile
}
}
|
Tasks configured for running with the JaCoCo agent delete the destination file for the execution data when the task starts executing. This ensures that no stale coverage data is present in the execution data. |
Default values of the JaCoCo Task extension
tasks.test {
configure<JacocoTaskExtension> {
isEnabled = true
destinationFile = layout.buildDirectory.file("jacoco/${name}.exec").get().asFile
includes = emptyList()
excludes = emptyList()
excludeClassLoaders = emptyList()
isIncludeNoLocationClasses = false
sessionId = "<auto-generated value>"
isDumpOnExit = true
classDumpDir = null
output = JacocoTaskExtension.Output.FILE
address = "localhost"
port = 6300
isJmx = false
}
}
test {
jacoco {
enabled = true
destinationFile = layout.buildDirectory.file("jacoco/${name}.exec").get().asFile
includes = []
excludes = []
excludeClassLoaders = []
includeNoLocationClasses = false
sessionId = "<auto-generated value>"
dumpOnExit = true
classDumpDir = null
output = JacocoTaskExtension.Output.FILE
address = "localhost"
port = 6300
jmx = false
}
}
While all tasks of type Test are automatically enhanced to provide coverage information when the java plugin has been applied, any task that implements JavaForkOptions can be enhanced by the JaCoCo plugin. That is, any task that forks Java processes can be used to generate coverage information.
For example, you can configure your build to generate code coverage using the application plugin:
plugins {
application
jacoco
}
application {
mainClass = "org.gradle.MyMain"
}
jacoco {
applyTo(tasks.run.get())
}
tasks.register<JacocoReport>("applicationCodeCoverageReport") {
executionData(tasks.run.get())
sourceSets(sourceSets.main.get())
}
plugins {
id 'application'
id 'jacoco'
}
application {
mainClass = 'org.gradle.MyMain'
}
jacoco {
applyTo run
}
tasks.register('applicationCodeCoverageReport', JacocoReport) {
executionData run
sourceSets sourceSets.main
}
.
└── build
├── jacoco
│ └── run.exec
└── reports
└── jacoco
└── applicationCodeCoverageReport
└── html
└── index.html
The example above targets the run task directly.
To target a TaskCollection instead — every JavaExec task, or a filtered subset — accumulate each task’s execution data via configureEach and pass the resulting FileCollection to executionData.
Calling executionData(TaskCollection) inside tasks.register {} trips Gradle’s mutation guard, so the collection must be built up outside the registration action:
val runTasks = tasks.withType<JavaExec>().matching { it.name == "run" }
jacoco {
applyTo(runTasks)
}
val runCoverageData = files()
runTasks.configureEach {
val jacocoExt = extensions.getByType<JacocoTaskExtension>()
runCoverageData.from(provider { jacocoExt.destinationFile })
}
tasks.register<JacocoReport>("applicationCodeCoverageReport") {
dependsOn(runTasks)
executionData(runCoverageData)
sourceSets(sourceSets.main.get())
}
def runTasks = tasks.withType(JavaExec).matching { it.name == 'run' }
jacoco {
applyTo runTasks
}
def runCoverageData = files()
runTasks.configureEach {
def jacocoExt = extensions.getByType(JacocoTaskExtension)
runCoverageData.from(provider { jacocoExt.destinationFile })
}
tasks.register('applicationCodeCoverageReport', JacocoReport) {
dependsOn runTasks
executionData runCoverageData
sourceSets sourceSets.main
}
Aggregating coverage across multiple test suites
A project may run more than one JVM test suite — for example the built-in test suite plus an integrationTest suite.
Each suite has its own Test task that produces its own execution data, and the default jacocoTestReport task only covers the test suite.
For coverage aggregated across projects, apply the JaCoCo Report Aggregation plugin.
Note that it produces one report per test suite name (for example testCodeCoverageReport and integrationTestCodeCoverageReport); it does not merge different suites into a single report.
To produce a single report covering all of a project’s test suites, register a JacocoReport task and feed it the execution data of every Test task.
Two constraints shape the pattern: executionData(TaskCollection) cannot be called inside tasks.register {} (Gradle’s mutation guard blocks it), so accumulate each task’s execution data via configureEach; and executionData only adds a mustRunAfter ordering, so declare an explicit dependsOn to make the suites actually run:
val testTasks = tasks.withType<Test>()
val testCoverageData = files()
testTasks.configureEach {
val jacocoExt = extensions.getByType<JacocoTaskExtension>()
testCoverageData.from(provider { jacocoExt.destinationFile })
}
tasks.register<JacocoReport>("codeCoverageReport") {
dependsOn(testTasks) // run every test suite first
executionData(testCoverageData) // collect each suite's coverage data
sourceSets(sourceSets.main.get())
}
def testTasks = tasks.withType(Test)
def testCoverageData = files()
testTasks.configureEach {
def jacocoExt = extensions.getByType(JacocoTaskExtension)
testCoverageData.from(provider { jacocoExt.destinationFile })
}
tasks.register('codeCoverageReport', JacocoReport) {
dependsOn testTasks // run every test suite first
executionData testCoverageData // collect each suite's coverage data
sourceSets sourceSets.main
}
The same pattern applies to JacocoCoverageVerification when you want a single coverage-threshold check across all suites.
Tasks
For projects that also apply the Java Plugin, the JaCoCo plugin automatically adds the following tasks:
jacocoTestReport— JacocoReport-
Generates code coverage report for the test task.
jacocoTestCoverageVerification— JacocoCoverageVerification-
Verifies code coverage metrics based on specified rules for the test task.
Dependency management
The JaCoCo plugin adds the following dependency configurations:
| Name | Meaning |
|---|---|
|
The JaCoCo Ant library used for running the |
|
The JaCoCo agent library used for instrumenting the code under test. |
Outgoing Variants
When a project producing JaCoCo coverage data is applied alongside the JVM Test Suite Plugin, additional outgoing variants will be created. These variants are designed for consumption by the JaCoCo Report Aggregation Plugin.
The attributes will resemble the following. User-configurable attributes are highlighted below the sample.
--------------------------------------------------
Variant coverageDataElementsForTest (i)
--------------------------------------------------
Binary results containing Jacoco test coverage for all targets in the 'test' Test Suite.
Capabilities
- org.gradle.sample:application:1.0.2 (default capability)
Attributes
- org.gradle.category = verification
- org.gradle.testsuite.name = test (1)
- org.gradle.verificationtype = jacoco-coverage
Artifacts
- build/jacoco/test.exec (artifactType = binary)
| 1 | TestSuiteName attribute; value is derived from TestSuite#getName(). |