Junit5: JUnit working with Gradle and Java Modules

Created on 22 Nov 2019  路  6Comments  路  Source: junit-team/junit5

Hello everyone,

I'm dealing with the following issue: I have got a JavaFX JDK 11 project with two modules: main and test. Main holds all the source code and the source code resources. And test holds all the JUnit tests and the related resources. Everything used to work fine before when I ran "gradlew test" with the useJUnitPlatform() command. But as soon as I added a module-info to my main module things started going downhill.

For a start, I would like to point out that adding this module-info.java file is a required part of building my application, so the solution of "just remove it!" would work to get the tests back working, but sadly isn't possible.

Following days of research I am not able to solve the issue myself and I have been stuck on the same set of errors for days:

C:...\src\test\java\module-info.java:1: error: module name FP.EDM.test does not match expected name FP.EDM.main
open module FP.EDM.test {
^
C:...\src\test\java\module-info.java:3: error: cyclic dependence involving FP.EDM.main
requires FP.EDM.main;
^
error: cannot access module-info
cannot resolve modules
warning: module name in --add-reads option not found: FP.EDM.main
3 errors
1 warning

I have got the following module-info.java file in my main module:

open module FP.EDM.main {
    //Exposes the main class for distribution
    exports edm;

    //Exposes all tested classes to the test module
    exports edm.utilities;
    exports edm.model.enumerations;
    exports edm.writers;
    exports edm.model;
    exports edm.handlers;
    exports edm.readers.importFileReaders;
    exports edm.model.interfaces;

    //JavaFX requirements
    requires javafx.graphics;
    requires javafx.controls;
    requires javafx.fxml;

    //Required for getting desktop metadata
    requires java.desktop;

    //Reading JSON files
    requires json;

    //Reading excel files
    requires poi;
    requires poi.ooxml;
    requires xmlbeans;
    requires jdk.charsets;

    //Ribbon
    requires fxribbon;

    //Reading CSV files
    requires commons.csv;

    //Required for doing automatic timestamp calculations
    requires org.apache.commons.lang3;
}

And the module-info.java below is part of the test module:

open module FP.EDM.test {
    requires org.junit.jupiter.api;
    requires FP.EDM.main;
}

The thing of which I believe causes the issue is the fact that useJUnitPlatform uses the main module whenever I print the module above the test command. So I have been looking for a way to set the module to the test module, but have been unable to find such a thing. That's the reason why I believe the issue belongs here on GitHub rather than any other help website.

Please feel free to request additional information, for a start I have also included my build.gradle below.

plugins {
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.8'
    id 'org.beryx.jlink' version '2.16.4'
}

version '1.0.0'
sourceCompatibility = 11

repositories {
    maven { url "https://dl.bintray.com/dukke/maven" }
    maven { url "https://repo.maven.apache.org/maven2/" }
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.6.0-M1'
    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.4.2')
    compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.0'
    compile 'com.pixelduke:fxribbon:1.2.1'
    compile group: 'org.json', name: 'json', version: '20160810'
    compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'
    compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.4'
    compile group: 'org.apache.commons', name: 'commons-compress', version: '1.19'
    compile group: 'org.apache.commons', name: 'commons-csv', version: '1.7'
    compile group: 'org.apache.poi', name: 'poi', version: '4.1.1'
    compile group: 'org.apache.poi', name: 'poi-ooxml', version: '4.1.1'
}

jlink {
    mergedModule {
        requires "java.xml"
        requires "javafx.controls"
    }
    addExtraDependencies("javafx")
    launcher {
        name = 'gPROMS EDM'
    }
}

tasks.jlink.doLast {
    copy {
        from('src/main/resources')
        into("$buildDir/image/bin/src/main/resources")
    }
}

test {
    println(moduleName)
    useJUnit()
}

javafx {
    version = "11.0.2"
    modules = [ 'javafx.controls', 'javafx.fxml' ]
}

application {
    mainClassName = 'FP.EDM.main/edm.Main'
}
Gradle Java 9+10+11... question

Most helpful comment

@SSchepers My proposed solution is: "just remove it!" 馃槇
But I'm not talking about the module-info.java in your main module. The problem is the one in the src/test/java directory.

When doing whitebox testing, you don't want (and, due to split packages, you cannot build) a separate "test module". Instead, for testing purposes, you need to patch your main module with the classes produced by compiling the code in src/test/java.

You use org.openjfx.javafxplugin, which automatically applies the gradle-modules-plugin. And this plugin patches the main module for you. It also detects the test framework used by your application and configures the necessary compiler flags for it (--add-modules, --add-reads, --add-opens).

As pointed by @sormuras, your build.gradle contains a strange mixture of JUnit 4 and 5. I'm not sure if the gradle-modules-plugin can handle this correctly.

If your tests need to access types from modules not required by your main module, you can pass the necessary additional compiler flags in a module-info.test file (this feature has been contributed by @sormuras). For example, if you use AssertJ in your tests, you need to put this in your module-info.test:

--add-modules
  org.assertj.core

--add-reads
  FP.EDM.main=org.assertj.core

So, my proposed solution is:

  1. Adjust your build.gradle and your test code to use only JUnit 5.
  2. Remove src/test/java/module-info.java
  3. If necessary, create a module-info.test file in src/test/java, with additional compiler flags for test.

All 6 comments

Did you give https://github.com/java9-modularity/gradle-modules-plugin a shot? It should take care of most Gradle-related configuration issues out-of-the-box and it offers a bunch of configuration options to tweak the compile and runtime settings.

@siordache's input might be of help here -- if you (@SSchepers) provide a mcve with less dependencies and moving parts involved. Speaking of dependencies:

    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.6.0-M1'
    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.4.2')

This is a very strange mixture of components. It should look more like this (test-wise):

https://github.com/junit-team/junit5-samples/blob/master/junit5-jupiter-starter-gradle-groovy/build.gradle#L11

@SSchepers My proposed solution is: "just remove it!" 馃槇
But I'm not talking about the module-info.java in your main module. The problem is the one in the src/test/java directory.

When doing whitebox testing, you don't want (and, due to split packages, you cannot build) a separate "test module". Instead, for testing purposes, you need to patch your main module with the classes produced by compiling the code in src/test/java.

You use org.openjfx.javafxplugin, which automatically applies the gradle-modules-plugin. And this plugin patches the main module for you. It also detects the test framework used by your application and configures the necessary compiler flags for it (--add-modules, --add-reads, --add-opens).

As pointed by @sormuras, your build.gradle contains a strange mixture of JUnit 4 and 5. I'm not sure if the gradle-modules-plugin can handle this correctly.

If your tests need to access types from modules not required by your main module, you can pass the necessary additional compiler flags in a module-info.test file (this feature has been contributed by @sormuras). For example, if you use AssertJ in your tests, you need to put this in your module-info.test:

--add-modules
  org.assertj.core

--add-reads
  FP.EDM.main=org.assertj.core

So, my proposed solution is:

  1. Adjust your build.gradle and your test code to use only JUnit 5.
  2. Remove src/test/java/module-info.java
  3. If necessary, create a module-info.test file in src/test/java, with additional compiler flags for test.

Thanks a lot, Serban! Much appreciated.

Alright thank you everyone for your amazing support. I've managed to solve it in a way I am happy with. Let me clarify what I did and what caused confusion on my part to help anyone in the future dealing with the same issue.

As @siordache pointed out, and what I was not aware of, org.openjfx.javafxplugin automatically applies the gradle-modules-plugin which patches the module. Therefore in my specific case, I didn't further investigate @sormuras provided possible solution as I did not want to mess with the existing automatic configuration.

Thank you both for pointing out the mixture in jUnit dependencies, I know where it comes from (the project contains jUnit 4 tests as well) but using the correct dependencies as pointed out by @siordache you are able to run both jUnit 4 and jUnit5 tests using useJUnitPlatform().

Finally what solved it for me, adding a module-info.java to your main module adds some difficulty with exposing dependencies, ignoring this file would technically work, but is not the nicest solution obviously. So adding the module-info.test is a lot better.

On the topic of module-info.test

  • The fact that my IDE (IntelliJ) did not recognize the type and said there was an error led me to be confused, so for anyone else dealing with this: don't worry about the red wrinkles.
  • I had expected module-info.test to be similar in structure to module-info.java, this is not true, these files contains stuff like --add-modules like pointed out by @siordache.
  • My test dependencies needed the jUnit platform (org.junit.platform) to be provided by hand to run

Hopefully this answer has been useful, again thank you!

my IDE (IntelliJ) did not recognize the type and said there was an error ...

IntelliJ will soon add support for module-info.test files: https://youtrack.jetbrains.com/issue/IDEA-222831

Closing this issue as "question-answered". Further discussion should move to Gradle's and/or IJ IDEA's issue tracker.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

solter picture solter  路  4Comments

netzwerg picture netzwerg  路  6Comments

mkobit picture mkobit  路  5Comments

otrosien picture otrosien  路  4Comments

timandy picture timandy  路  3Comments