Problem

I want to categorise my tests, but I also want to see their respective code coverage using Jacoco.

Solution

Tested on Gradle v6.0.1.

When all your tests live in the test source set, you can simply follow the documentation. However, if you structure your tests more granularly, like this:

.
├── src
│   ├── cdcTest
│   │   ├── java
│   │   └── resources
│   ├── e2eTest
│   │   ├── java
│   │   └── resources
│   ├── intTest
│   │   ├── java
│   │   └── resources
│   ├── main
│   │   ├── java
│   │   └── resources
│   └── test
│       ├── java
│       └── resources

Then you will need to create additional sourcesets. I like to store these as build scripts, but you can put all this in one build.gradle if that suits your use case.

// File: ./gradle/integration-test.gradle
sourceSets {
    intTest {
        compileClasspath += sourceSets.main.output
        runtimeClasspath += sourceSets.main.output
    }
}

configurations {
    intTestImplementation.extendsFrom implementation
    intTestRuntimeOnly.extendsFrom runtimeOnly
}

dependencies {
    intTestImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
    intTestImplementation 'junit:junit:4.13'
    intTestImplementation 'com.github.tomakehurst:wiremock:2.25.1'
}

task integrationTest(type: Test) {
    description = 'Runs the integration tests.'
    group = 'verification'

    testClassesDirs = sourceSets.intTest.output.classesDirs
    classpath = sourceSets.intTest.runtimeClasspath
    shouldRunAfter test

    outputs.upToDateWhen { false }

    testLogging {
        events "passed", "skipped", "failed"
    }
}

check.dependsOn integrationTest

Then I apply this in build.gradle like so:

// File: ./build.gradle
...
repositories {
	jcenter()
}

apply from: "$rootDir/gradle/integration-test.gradle"
apply from: "$rootDir/gradle/contract-test.gradle"
apply from: "$rootDir/gradle/e2e-test.gradle"

...

You can see in this example I have a few different types of test sources. To do the same it’s simply a rinse and repeat. I could use a list to generate multiple source sets programatically, but I prefer to be verbose here.

For code coverage we just need to update ./gradle/integration-test.gradle with the following block.

// File: ./gradle/integration-test.gradle
...
task jacocoIntTestReport(type: JacocoReport) {
    description = 'Generates code coverage report for the integrationTest task.'
    group = 'verification'

    sourceSets sourceSets.main
    executionData integrationTest

    reports {
        xml.enabled true
    }

    mustRunAfter integrationTest
}
...