在具有模块间测试依赖性的Maven构建中正确实现Java模块

har*_*ldK 19 java junit module maven java-9

我有一个使用Maven和Java的多模块项目.我现在正在尝试迁移到Java 9/10/11并实现模块(如JSR 376:Java平台模块系统,JPMS).由于项目已经由Maven模块组成,并且依赖项是直接的,因此为项目创建模块描述符非常简单.

每个Maven模块现在module-info.javasrc/main/java文件夹中都有自己的模块描述符().测试类没有模块描述符.

但是,我偶然发现了一个我无法解决的问题,并没有找到任何关于如何解决的说明:

如何使用Maven和Java模块进行模块间测试依赖?

在我的例子中,我有一个"通用"Maven模块,它包含一些接口和/或抽象类(但没有具体的实现).在相同的Maven模块中,我有抽象测试来确保这些接口/抽象类的实现的正确行为.然后,有一个或多个子模块,接口/抽象类的实现和扩展抽象测试的测试.

但是,在尝试执行testMaven构建阶段时,子模块将失败并显示:

[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
Run Code Online (Sandbox Code Playgroud)

我怀疑这是因为测试不是模块的一部分.即使Maven在模块范围内执行测试还有一些"魔力",但它不适用于我依赖的模块中的测试(出于某种原因).我该如何解决?

项目的结构如下所示(此处提供完整的演示项目文件):

????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
Run Code Online (Sandbox Code Playgroud)

依赖关系my-impl-module/pom.xml如下:

<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>
Run Code Online (Sandbox Code Playgroud)

注意:以上只是我为展示问题而创建的项目.真正的项目要复杂得多,在这里找到(主分支还没有模块化),但原理是一样的.

PS:我认为代码本身没有任何问题,因为所有内容都使用普通的类路径进行编译和运行(即在IntelliJ中,或者在没有Java模块描述符的情况下使用Maven).Java模块和模块路径引入了该问题.

Jos*_*uez 6

根据您的演示项目,我能够复制您的错误.也就是说,这是我在第一次尝试失败后所做的修改后的更改,以便能够构建项目:

  1. 我在maven-compiler-plugin所有模块中添加了3.8.0版本.您需要3.7或更高版本才能使用Maven编译模块 - 至少这是NetBeans所显示的警告.由于没有任何危害,我在常用模块和实现模块的POM文件中添加了插件:

    <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> 
    
    Run Code Online (Sandbox Code Playgroud)
  2. 我将测试类导出到他们自己的jar文件中,以便它们可供您的实现模块或任何人使用.为此,您需要将以下内容添加到您的my-common-module/pom.xml文件中:

    <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>
    
    Run Code Online (Sandbox Code Playgroud)

    这会将my-common-module测试类导出到-tests.jar文件中 - 即my-common-module-1.0-SNAPSHOT-tests.jar.请注意,没有必要添加的用于执行定期jar在此指出的文件.但是,这会引入我接下来要解决的错误.

  3. 将测试包重命名为my-common-moduleto com.example.common.test,以便在编译实现测试类时加载测试类.这解决了导出测试类时引入的类加载问题,该测试类具有与第一个jar(在本例中为模块)加载的模块中相同的包名称,而第二个jar是测试jar文件被忽略.有趣的是,基于观察,我得出结论,模块路径的优先级高于类路径,因为Maven编译参数显示tests.jar在类路径中首先指定.运行mvn clean validate test -X,我们看到编译参数:

    -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
    
    Run Code Online (Sandbox Code Playgroud)
  4. 我们需要使导出的测试类可用于实现模块.将此依赖项添加到您的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>
    
    Run Code Online (Sandbox Code Playgroud)
  5. 最后在my-impl-module测试类中,更新导入以指定新的测试包com.example.common.text,以访问my-common-module测试类:

    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 { ... }
    
    Run Code Online (Sandbox Code Playgroud)

以下是我mvn clean package对新变化的测试结果:

在此输入图像描述

我在我的java-cross-module-testing GitHub repo中更新了我的示例代码.我唯一挥之不去的问题,我确信你也这么做,这就是为什么当我将实现模块定义为常规jar项目而不是模块时它是否有效.但那,我将在其他日子玩.希望我提供的解决了你的问题.