You can open this sample inside an IDE using the IntelliJ’s Gradle import or Eclipse Buildship.

This sample shows how to create a multi-project containing Java Modules. Java Modules are a feature of Java itself, available since Java 9, that allows for better encapsulation.

In Gradle, each source set containing Java sources can be turned into a module by adding a module-info.java file. Typically, in a project with Java Modules like this one, the main source set of a subproject represents a module.

src
└── main
    └── java
        └── module-info.java

In the module-info.java file you define dependencies to other modules using keywords like requires or requires transitive. These correspond to the implementation and api dependencies defined in the Gradle build file. In addition, a module exports packages that should be visible to consumers. Other packages are not visible outside of the module.

module org.gradle.sample.utilities {
    requires transitive org.gradle.sample.list;
    exports org.gradle.sample.utilities;
}

Unit (whitebox) tests that need to access the internals of a module can be written in the traditional way by not adding a module-info.java to the test source set. In test execution, the modules are then treated as standard Java libraries with the encapsulation deactivated.

Blackbox (e.g. integration) tests, which should also follow the encapsulation rules during test execution, can be written by turning the corresponding test sources set itself into a module by adding a module-info.java. This is shown in this extended sample.

As of Gradle 6.4, module support needs to be explicitly activated, which we do for all subprojects in the root build script like this:

build.gradle
    plugins.withType(JavaPlugin).configureEach {
        java {
            modularity.inferModulePath = true
        }
    }
build.gradle.kts
    plugins.withType<JavaPlugin>().configureEach {
        configure<JavaPluginExtension> {
            modularity.inferModulePath.set(true)
        }
    }