Properly implementing Java modules in a Maven build with inter-module test dependencies
Based on your demo project, I was able to duplicate your error. That said, here are the revised changes I made, after my first failed attempt, to be able to build the project:
I added the
maven-compiler-plugin
version 3.8.0 to all the modules. You need a version of 3.7 or higher to compile modules with Maven - at least that's the warning NetBeans showed. Since there is no harm, I added the pluging to both the common and implementation modules' POM files:<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <executions> <execution> <goals> <goal>compile</goal> </goals> <id>compile</id> </execution> </executions> </plugin>
I exported the test classes into their own
jar
file so they will be available to your implementation module or anyone for that matter. To do that, you need to add the following to yourmy-common-module/pom.xml
file:<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <id>test-jar</id> <phase>package</phase> <goals> <goal>test-jar</goal> </goals> </execution> </executions> </plugin>
This will export
my-common-module
test classes into-tests.jar
file - i.e.my-common-module-1.0-SNAPSHOT-tests.jar
. Notice there is no need to add an execution for the regularjar
file as noted in this post. This will, however, introduced error that I will address next.Rename your test package in
my-common-module
tocom.example.common.test
in order for the test classes to be loaded when compiling the implementation test class(es). This corrects the class load issue introduced when we exported the test classes with the same package name as in the module where the firstjar
, in this case the module, is loaded and the secondjar
, the test jar file, is ignored. Interesting enough, I'm concluding, based on observation, that the module path has higher precedence than the class path since the Maven compile parameters shows thetests.jar
is specified first in the class path. Runningmvn clean validate test -X
, we see compile parameters:-d /home/testenv/NetBeansProjects/MavenProject/Implementation/target/test-classes -classpath /home/testenv/NetBeansProjects/MavenProject/Implementation/target/test-classes:/home/testenv/.m2/repository/com/example/Declaration/1.0-SNAPSHOT/Declaration-1.0-SNAPSHOT-tests.jar:/home/testenv/.m2/repository/junit/junit/4.12/junit-4.12.jar:/home/testenv/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar: --module-path /home/testenv/NetBeansProjects/MavenProject/Implementation/target/classes:/home/testenv/.m2/repository/com/example/Declaration/1.0-SNAPSHOT/Declaration-1.0-SNAPSHOT.jar: -sourcepath /home/testenv/NetBeansProjects/MavenProject/Implementation/src/test/java:/home/testenv/NetBeansProjects/MavenProject/Implementation/target/generated-test-sources/test-annotations: -s /home/testenv/NetBeansProjects/MavenProject/Implementation/target/generated-test-sources/test-annotations -g -nowarn -target 11 -source 11 -encoding UTF-8 --patch-module example.implementation=/home/testenv/NetBeansProjects/MavenProject/Implementation/target/classes:/home/testenv/NetBeansProjects/MavenProject/Implementation/src/test/java:/home/testenv/NetBeansProjects/MavenProject/Implementation/target/generated-test-sources/test-annotations: --add-reads example.implementation=ALL-UNNAMED
We need to make the exported test classes available to the implementation module. Add this dependency to your
my-impl-module/pom.xml
:<dependency> <groupId>com.example</groupId> <artifactId>Declaration</artifactId> <version>1.0-SNAPSHOT</version> <type>test-jar</type> <scope>test</scope> </dependency>
Lastly in the
my-impl-module
test class, update the import to specify the new test package,com.example.common.text
, to access themy-common-module
test classes:import com.example.declaration.test.AbstractFooTest; import com.example.declaration.Foo; import org.junit.Test; import static org.junit.Assert.*; /** * Test class inheriting from common module... */ public class FooImplementationTest extends AbstractFooTest { ... }
Here is the test results from my mvn clean package
of the new changes:
I updated my sample code in my java-cross-module-testing GitHub repo. The only lingering question I have, and I'm sure you do as well, is why did it worked when I defined the implementation module as a regular jar
project instead of a module. But that, I'll play with some other day. Hopefully what I provided solves your problem.