I have a multi-module project using Maven and Java. I am now trying to migrate to Java 9/10/11 and implement modules (as in JSR 376: Java Platform Module System, JPMS). As the project was already consisting of Maven modules, and the dependencies were straight, creating module descriptors for the project was quite straight forward.
Each Maven module now has their own module descriptor (module-info.java
), in the src/main/java
folder. There is no module descriptor for the test classes.
However, I stumbled upon a problem I have not been able to solve, and not found any descriptions on how to solve:
How can I have inter-module test dependencies with Maven and Java modules?
In my case, I have a "common" Maven module, which contains some interfaces and/or abstract classes (but no concrete implementation). In the same Maven module, I have abstract tests to ensure proper behavior for the implementation of these interfaces/abstract classes. Then, there are one or more sub modules, with implementations of the interface/abstract class and tests extending the abstract test.
However, when trying to execute the test
phase of the Maven build, the sub module will fail with:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.0:testCompile (default-testCompile) on project my-impl-module: Compilation failure: Compilation failure:
[ERROR] C:\projects\com.example\my-module-test\my-impl-module\src\test\java\com\example\impl\FooImplTest.java:[4,25] error: cannot find symbol
[ERROR] symbol: class FooAbstractTest
[ERROR] location: package com.example.common
I suspect that this happens because the tests are not part of the module. And even if Maven does some "magic" to get the tests executed within the scope of the module, it doesn't work for the tests in the module I depend on (for some reason). How do I fix this?
The structure of the project looks like this (full demo project files available here):
├───my-common-module
│ ├───pom.xml
│ └───src
│ ├───main
│ │ └───java
│ │ ├───com
│ │ │ └───example
│ │ │ └───common
│ │ │ ├───AbstractFoo.java (abstract, implements Foo)
│ │ │ └───Foo.java (interface)
│ │ └───module-info.java (my.common.module: exports com.example.common)
│ └───test
│ └───java
│ └───com
│ └───example
│ └───common
│ └───FooAbstractTest.java (abstract class, tests Foo)
├───my-impl-module
│ ├───pom.xml
│ └───src
│ ├───main
│ │ └───java
│ │ ├───com
│ │ │ └───example
│ │ │ └───impl
│ │ │ └───FooImpl.java (extends AbstractFoo)
│ │ └───module-info.java (my.impl.module: requires my.common.module)
│ └───test
│ └───java
│ └───com
│ └───example
│ └───impl
│ └───FooImplTest.java (extends FooAbstractTest)
└───pom.xml
Dependencies in the my-impl-module/pom.xml
is as follows:
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-common-module</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-common-module</artifactId>
<classifier>tests</classifier> <!-- tried type:test-jar instead, same error -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Note: The above is just a project I created to demonstrate the problem. The real project is a lot more complex, and found here (master branch is not modularized yet), but the principle is the same.
PS: I don't think there's anything wrong with the code itself, as everything compiles and runs using normal class path (ie. in IntelliJ, or Maven without the Java module descriptors). The problem is introduced with Java modules and the module path.
my-common-module
and then change <classifier>tests</classifier> per <type>test-jar</type> in themy-impl-module/pom.xml
my-common-module dependency. I've tried and it works well. – Folietest-jar
dependency. – Snippet