BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News Gradle 7.4 Introduces Aggregated Test Reports

Gradle 7.4 Introduces Aggregated Test Reports

This item in japanese

Lire ce contenu en français

Bookmarks

Gradle has released version 7.4 of their open-source build automation tool that allows developers to create aggregated test and Jacoco coverage HTML reports. The versions catalog feature allows the centralized declaration of dependencies for usage in build scripts. Shared build services allow caching of state or resources across multiple tasks.

To enable these latest features, the Gradle Wrapper can be configured to use Gradle 7.4:

./gradlew wrapper --gradle-version=7.4

The new test-report-aggregation plugin makes it possible, together with the JVM Test Suite Plugin, to combine the test results of multiple Test tasks, even across Gradle projects into one HTML report. The plugin automatically creates an aggregated test report:

plugins {
    id 'test-report-aggregation'
    …
}

dependencies {
    implementation project(':repositories')
    ...
}

application {
    mainClass = 'org.gradle.sample.Main'
}

tasks.named('check') {
    dependsOn tasks.named('testAggregateTestReport', TestReport) 
}

It’s possible to configure which project and test suites should be used. After executing ./gradlew testAggregateTestReport, the HTML report can be found in the application/build/reports/tests/unit-tests/aggregated-results folder.

The new jacoco-report-aggregation plugin is comparable to the test-report-aggregation plugin as it combines JaCoCo code coverage reports, possibly from multiple projects, into one HTML report based on test suites using the JVM Test Suite Plugin. This plugin also automatically creates an aggregated test report:

plugins {
    id 'jacoco-report-aggregation'
    …
}

dependencies {
    implementation project(':repositories')
    ...
}

application {
    mainClass = 'org.gradle.sample.Main'
}

tasks.named('check') {
    dependsOn tasks.named('testCodeCoverageReport', JacocoReport) 
}

Again, it’s possible to configure which project and test suites should be used. After executing ./gradlew testCodeCoverageReport, the XML and HTML report can be found under the application/build/reports/jacoco/testCodeCoverageReport folder

The version catalogs feature is now stable and allows the declaration of dependencies and their versions for sharing across projects. The version catalogs are defined in the settings.gradle or settings.gradle.kts files:

dependencyResolutionManagement {
    versionCatalogs {
        libs {
            library('micronaut-http', 'io.micronaut:micronaut-http-client:3.3.3')
        }
    }
}

It’s possible to declare and reuse a specific version for multiple components:

dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version('micronaut', '3.3.3'
            library('micronaut-http', 'io.micronaut', 
                'micronaut-http-client').versionRef('micronaut')
	        library('micronaut-runtime', 'io.micronaut',
                'micronaut-runtime').versionRef('micronaut')
        }
    }
}

The version catalog may be used in the build.gradle file where libs is a version catalog and micronaut-http and micronaut-runtime are the names of the dependencies in the catalog:

dependencies {
    implementation libs.micronaut-http
    implementation libs.micronaut-runtime
}

Several dependencies may be combined into a bundle:

dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version('micronaut', '3.3.3'
            library('micronaut-http', 'io.micronaut', 
                'micronaut-http-client').versionRef('micronaut')
            library('micronaut-runtime', 'io.micronaut',
                'micronaut-runtime').versionRef('micronaut')
            bundle('micronaut', ['micronaut-http', 'micronaut-runtime'])
        }
    }
}

A bundle simplifies the configuration of the build.gradle:

dependencies {
    implementation libs.bundles.micronaut
}

Alternatively, the catalog may be stored as a TOML file. By default, the libs.versions.toml file will be read into a libs catalog:

[versions]
micronaut = "3.3.3"

[libraries]
micronaut-http = 
    { module = "io.micronaut:micronaut-http-client", version.ref = "micronaut" }
micronaut-runtime = 
    { module = "io.micronaut:micronaut-http-client", version.ref = "micronaut" }

[bundles]
micronaut = ["micronaut-http", "micronaut-runtime"]

[plugins]
…

The user guide details all possible options such as the configuration for plugins and the possibility to use multiple catalogs.

Cédric Champeau, Micronaut and GraalVM team member at Oracle Labs, explains how the versions catalogs feature may be used to manage Micronaut dependencies. Micronaut already ships a version catalog as an alternative for the BOM. The Micronaut version catalog may be imported, from the BOM location in Maven central, in a Micronaut project on Gradle 7.4:

dependencyResolutionManagement {
    repositories {
        mavenCentral()
    }
    versionCatalogs {
        create("mn") {
            from("io.micronaut:micronaut-bom:${micronautVersion}")
        }
    }
}

The new version catalog, referred to as mn, may be used in the build.gradle file. The Kotlin DSL additionally offers code completion and the dependency notations are type-safe.

Build services is another, new stable feature that allows sharing state or resources between tasks. A build service is an object which holds state such as caching or database instances. Gradle automatically creates and removes the build service. The build service is created by implementing the BuildService interface and optionally defining one or more custom parameters:

public abstract class CacheBuildService implements BuildService<CacheBuildService.Params> {

    interface Params extends BuildServiceParameters {
        Property<Integer> getCustomParameter();
    }

    private final Map<String, Integer> cachingMap = new HashMap();;

    public CacheBuildService() {
        int customerParameter = getParameters().getCustomParameter().get();
        cachingMap.put("James", customerParameter)

        System.out.println(String.format("BuildService: Cache contains value %s", 
            cachingMap.get("James")));
    }

    public Map<String, Integer> getCachingMap() {
        return cachingMap;
    }
}

Next, a task is created that uses the previously constructed CacheBuildService:

public abstract class CacheTask extends DefaultTask {
    @Internal
    abstract Property<CacheBuildService> getCache();

    @TaskAction
    public void useCache() {
        CacheBuildService cache = getCache().get();
        Map<String, Integer> cachingMap = cache.getCachingMap();
        System.out.println(String.format("Task: Cache contains value %s", 
            cachingMap.get("James")));
    }
}

The last step involves defining the plugin and supplying the optional custom parameters and connecting the task to the CacheBuildServiceProvider:

public class CachePlugin implements Plugin<Project> {
    public void apply(Project project) {
        Provider<CacheBuildService> cacheBuildServiceProvider = 
                project.getGradle().getSharedServices()
                .registerIfAbsent("cache", CacheBuildService.class, spec -> {
            spec.getParameters().getCustomParameter().set(42);
    });

    project.getTasks().register("cache-example", CacheTask.class, task -> {
        task.getCache().set(cacheBuildServiceProvider);
        task.usesService(cacheBuildServiceProvider);
    });
  }
}

Executing the plugin with ./gradlew clean -q cache-example prints:

BuildService: Cache contains value 42
Task: Cache contains value 42

Several usability improvements were introduced. The IntelliJ IDEA Plugin now automatically marks all source directories used by JVM Test Suite Plugin as test source directories. Gradle’s dependency verification verifies the checksums of the plugins and dependencies of the project. The generation of the dependency verification file is more stable and the result is the same if the build configuration and the verification file don’t change.

Java toolchains allow configuring the desired Java version via the JvmVendorSpec class. New fields, ADOPTIUM and IBM_SEMERU, have been added as vendors due to the migration of AdoptOpenJDK to Eclipse Adoptium and the introduction of the IBM Semeru Runtimes. Using the ADOPTOPENJDK field will result in a deprecation warning.

The configuration cache was improved to automatically detect environment variables, system properties and Gradle properties instead of having to use the now deprecated Provider.forUseAtConfigurationTime() API.

The Kotlin DSL now creates type-safe model accessors for custom extensions in the repositories block. This makes it possible to create a more concise configuration, for example, by removing the withGroovyBuilder:

repositories {
    withGroovyBuilder { // This line is now optional
        "ruby" {
            "gems"()
        }
    }
}

Further details on all changes in version 7.4 are documented in the release notes.

About the Author

Rate this Article

Adoption
Style

BT