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:

  1. 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> 
    
  2. 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 your my-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 regular jar file as noted in this post. This will, however, introduced error that I will address next.

  3. Rename your test package in my-common-module to com.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 first jar, in this case the module, is loaded and the second jar, 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 the tests.jar is specified first in the class path. Running mvn 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
    
  4. 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>
    
  5. Lastly in the my-impl-module test class, update the import to specify the new test package, com.example.common.text, to access the my-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:

enter image description here

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.