JaCoCo Configuration using Gradle’s Kotlin DSL

Recently I used Kotlin DSL for writing Gradle scripts in a spring boot project. As Gradle’s Kotlin DSL is quite new, the documentation and community support was less compared to the default Groovy DSL. So I found it hard to configure JaCoCo for test coverage. In this blog I will share how I added test coverage in my project:

Before getting into the implementation let me put down the requirements:

  1. A test task to run all unit tests
  2. A testCoverage task that runs all unit tests, generate coverage report and run verification
  3. A way to ignore classes and files from coverage verification

1. A test task to run all unit tests

When using a JVM language plugin like Java in Gradle, a task called test is automatically provided. This task runs all tests under src/test by default.

We just have to add a config to run the tests using a test runner (JUnit in my case):

val test by tasks.getting(Test::class) {
    useJUnitPlatform { }
}

2. A testCoverage task that runs all unit tests, generate coverage report and verify the report

First, we need to add the JaCoCo library by adding it in the plugins section:

plugins {
    ...
    jacoco
}

By default, JaCoCo gives two tasks called jacocoTestReport and jacocoTestCoverageVerification for generating coverage report and running verification respectively.

As I need only HTML report I added a jacocoTestReport config like this:

tasks.jacocoTestReport {
    reports {
        xml.isEnabled = false
        csv.isEnabled = false
        html.isEnabled = true
        html.destination = file("$buildDir/reports/coverage")
    }
}

And I want to fail the task when the coverage falls below 90%. So I added a jacocoTestCoverageVerification like this:

tasks.jacocoTestCoverageVerification {
    violationRules {
        rule {
            limit {
                minimum = "0.9".toBigDecimal()
            }
        }
    }
}

We can generate coverage report and run verification by doing:

./gradlew test jacocoTestReport jacocoTestCoverageVerification

It will be good if we have a wrapper task called testCoverage that runs test, jacocoTestReport, and jacocoTestCoverageVerification. Let's add this new task:

val testCoverage by tasks.registering {
    group = "verification"
    description = "Runs the unit tests with coverage."

    dependsOn(":test", ":jacocoTestReport", ":jacocoTestCoverageVerification")
    val jacocoTestReport = tasks.findByName("jacocoTestReport")
    jacocoTestReport?.mustRunAfter(tasks.findByName("test"))
    tasks.findByName("jacocoTestCoverageVerification")?.mustRunAfter(jacocoTestReport)
}

Now to run we just have to do:

./gradlew testCoverage

3. A way to ignore classes and files from coverage verification

I want to ignore certain files and classes from the JaCoCo verification. To do that we can modify the classDirectories or sourceDirectories used by jacocoTestCoverageVerification task like this:

tasks.jacocoTestCoverageVerification {
    violationRules {
        rule {
            limit {
                minimum = "0.9".toBigDecimal()
            }
        }
    }
    classDirectories.setFrom(
            sourceSets.main.get().output.asFileTree.matching {
                // exclude main()
                exclude("com/example/helloworld/ApplicationKt.class")
            }
    )
}

Now if we run ./gradlew testCoverage, main() function will be excluded from verification.

A full gradle script containing similar implementation is available here: spring-boot-helloworld.