Sukhbir
Sukhbir

Reputation: 661

How to use Jacoco plugin along with JVM test suites plugin in Gradle?

We have a java project which is using gradle build tool. We are using jacoco plugin to generate test coverage report. Later we have added test suites using JVM Test Suite plugin of gradle. We have observed that all those test classes run by test suites are not getting covered in code coverage report of jacoco. So If anyone got some idea what exactly should i write in build.gradle file for accomplish this, that would be great. Thanks.

This is the build.gradle file snippet:-

plugins {
    id 'java'
    id 'jacoco'
    id 'jvm-test-suite'
}

test {
    useJUnitPlatform()
    finalizedBy jacocoTestReport
}

testing {
    suites {
        test {
            useJUnitJupiter()
        }

        customTest(JvmTestSuite) {
            dependencies {
                ... // some dependencies
            }
        }
    }
}

tasks.named('check') {
    dependsOn(testing.suites.customTest)
}

dependencies {
... // some other dependencies
}

jacocoTestReport {
    dependsOn test
    reports {
        xml.required = true
    }
}

Upvotes: 2

Views: 1919

Answers (1)

Kirill
Kirill

Reputation: 8088

Considering that you've defined an additional test suite (integrationTest) and added jacoco plugin as follows:

plugins {
    java
    `jvm-test-suite`
    jacoco
}

testing {
    suites {
        @Suppress("UnstableApiUsage")
        named<JvmTestSuite>("test") {
        useJUnitJupiter()
            testType.set(TestSuiteType.UNIT_TEST)
        }

        @Suppress("UnstableApiUsage")
        register<JvmTestSuite>("integrationTest") {
            useJUnitJupiter()
            testType.set(TestSuiteType.INTEGRATION_TEST)

            dependencies {
                configurations.named("integrationTestImplementation") {
                    extendsFrom(configurations.testImplementation.get())
                }
                implementation(project())
            }
        }
    }
}

each time you execute either test or integrationTest there is a corresponding jacoco exec file created at build/(test|integrationTest).exec

Now to get html-reports (or xml/csv-reports, depending on which one you've requested in tasks.jacocoTestReport {...} block), jacoco plugin adds jacocoTestReport task which transforms .exec file into other formats. The jacocoTestReport assumes that there is only one default test suite called test and thus only respects build/jacoco/test.exec.

As for now (Gradle 8.1.1), adding the jacoco-report-aggregation plugin to the build will automatically create corresponding task to convert .exec coverage data into other formats for any additional test suite you declare. In my example, after applying id("jacoco-report-aggregation"), I will automatically get integrationTestCodeCoverageReport task which will take care of transforming build/jacoco/integrationTest.exec into html and/or other formats.

Finally, if I want to have a single aggregated coverage report in html format, I need to use the testCodeCoverageReport task, which was also brought by introducing jacoco-report-aggregation to the build. Based on the description ("Generates aggregated code coverage report") the goal of this task is exactly report aggregation. But you need to adjust it's configuration before using, so it becomes aware of your custom test suite:

reporting {
    reports {
        @Suppress("UnstableApiUsage")
        named<JacocoCoverageReport>("testCodeCoverageReport") {
            reportTask {
                executionData.builtBy(tasks.test, tasks.named("integrationTest")) // adding dependencies between aggregate reporting and test tasks
                executionData.from( // specifying paths to process coverage in .exec format
                    project.layout.buildDirectory.file("jacoco/test.exec"),
                    project.layout.buildDirectory.file("jacoco/integrationTest.exec")
                )
            }
            testType.set("Aggregate")
        }
    }
}

Now to get a single aggregated coverage report in html, execute ./gadlew test integrationTest testCodeCoverageReport


UPD 2023-06-30

As for Gradle 8.1 here is the universal way to aggregate INTEGRATION_TEST coverage data in addition to UNIT_TEST coverage (without hard-coding paths to .exec files) and to produce a single report as a result:

reporting {
    reports {
        @Suppress("UnstableApiUsage")
        create<JacocoCoverageReport>("testCodeCoverageReportRoot") {
            testType.set(TestSuiteType.UNIT_TEST)
            reportTask {
                executionData.from(
                    configurations["aggregateCodeCoverageReportResults"]
                        .incoming.artifactView {
                            lenient(true)
                            withVariantReselection()
                            attributes {
                                attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.VERIFICATION))
                                attribute(TestSuiteType.TEST_SUITE_TYPE_ATTRIBUTE, objects.named(TestSuiteType.INTEGRATION_TEST))
                                attribute(VerificationType.VERIFICATION_TYPE_ATTRIBUTE, objects.named(VerificationType.JACOCO_RESULTS))
                                attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.BINARY_DATA_TYPE)
                            }
                        }.files,
                )
            }
        }
    }
}

If you perform coverage aggregation in a separate module, do not forget to add your source/test code modules as a dependencies with jacocoAggregation scope, like this:

dependencies {
    jacocoAggregation(project(":my-domain-model-with-unit-tests-only"))
    jacocoAggregation(project(":spring-boot-app-with-unit-and-integration-tests"))
}

Many thanks to Jendrik Johannes for his youtube video series "Understanding Gradle" and his great accompanied examples on GitHub

Upvotes: 2

Related Questions