Java – Gradle Jacoco plugin not generating reports

gradlejacocojava

Java 8 and Gradle 4.6 here. I'm trying to configure my Gradle build to use the Jacoco Plugin but am having some difficulty. I've already got it working with Checkstyle and Findbugs, such that running ./gradlew clean build invokes the Checkstyle and Findbugs tasks because they're dependencies of the check task.

I'm now trying to get Jacoco working such that:

  1. It excludes my com.me.myapp.domain.model package and all of its contents; and
  2. It fails my build if code coverage on the non-excluded classes falls below 70%; and
  3. Pass or fail, I want an HTML version of the Jacoco report generated under the build/ directory; and
  4. Ideally I could just use the same Gradle command invocation of ./gradlew clean build to get Jacoco working like this

My best attempt thus far:

plugins {
    id 'java-library'
    id 'checkstyle'
    id 'findbugs'
    id 'jacoco'
}

dependencies {
    compile(
        'org.hibernate:hibernate-core:5.0.12.Final'
        ,'com.fasterxml.jackson.core:jackson-core:2.8.10'
        ,'com.fasterxml.jackson.core:jackson-databind:2.8.10'
        ,'com.fasterxml.jackson.core:jackson-annotations:2.8.0'
    )

    testCompile(
        'junit:junit:4.12'
    )
}

repositories {
    jcenter()
    mavenCentral()
}

checkstyle {
    config = rootProject.resources.text.fromFile('buildConfig/checkstyle/checkstyle.xml')
    toolVersion = '8.11'
}

tasks.withType(FindBugs) {
    reports {
        xml.enabled false
        html.enabled true
    }
}

findbugs {
    excludeFilter = file('buildConfig/findbugs/findbugs-exclude.xml')
}

jacocoTestReport {
    reports {
        xml.enabled false
        csv.enabled false
        html.enabled true
    }

    afterEvaluate {
        classDirectories = files(classDirectories.files.collect {
            fileTree(dir: it,
                exclude: [
                    'com/me/myapp/domain/model/**'
                ]
            )
        })
    }
}

jacocoTestCoverageVerification {
    violationRules {
        rule {
            limit {
                minimum = 0.7
            }

            failOnViolation true
        }
    }
}

jacoco {
    toolVersion = "0.8.1"
}

// to run coverage verification during the build (and fail when appropriate)
check.dependsOn jacocoTestCoverageVerification

When I run ./gradlew clean build with the following build.gradle above (^^^) Jacoco does fail the build if my coverage is less than 70%. However it does not generate any HTML report for me, which is not helpful at all in terms of fixing it.

Any ideas?

Best Answer

Please note, that the Gradle Jacoco plugin does provide two totally unrelated functionalities:

If the plugin is applied together with the Java Plugin, a task of each of the mentioned types is created, namely jacocoTestReport and jacocoTestCoverageVerification. As you can see by the name both of them are associated with the test task.

However, none of those tasks is automatically included into the regular Gradle build lifecycle. The reason for not including the report task is simply because it's not necessary for actually building the actual software. For the same reason, the javadoc task is not included in the build lifecycle (it might be when creating a javadoc jar). The reason for not including the verification task is trickier, but let's simply quote the docs:

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. Future versions of Gradle might change the behavior.

You already solved this problem by adding check.dependsOn jacocoTestCoverageVerification to your build file. This way, the code coverage will be checked with each build (and fail if not sufficient). Now you want to generate the report in all builds, even if it fails due to unsufficient code coverage. You need to ensure that the report is generated before the build might fail. You could use:

jacocoTestCoverageVerification.dependsOn jacocoTestReport