当前位置: 首页 > 知识库问答 >
问题:

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

松兴邦
2023-03-14

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

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

但是,我偶然发现了一个我一直没能解决的问题,也没有找到任何关于如何解决的描述:

我如何才能与Maven和Java模块有模块间的测试依赖关系?

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

但是,当尝试执行Maven构建的test阶段时,子模块将失败,原因是:

[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

我怀疑发生这种情况是因为测试不是模块的一部分。而且即使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

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>

附注:我不认为代码本身有什么问题,因为所有的东西都是使用正常的类路径编译和运行的(即在IntelliJ或Maven中没有Java模块描述符)。通过Java模块和模块路径介绍了该问题。

共有1个答案

怀浩大
2023-03-14

根据您的演示项目,我能够复制您的错误。话虽如此,以下是我在第一次尝试失败后为了能够构建该项目所做的修改:

>

  • 我向所有模块添加了maven-compiler-plugin3.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> 
    

    我将测试类导出到它们自己的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>
    

    这将把my-common-module测试类导出到-tests.jar文件中,即my-common-module-1.0-snapshot-tests.jar。注意,不需要为常规的jar文件添加执行,如本文所述。然而,这个will引入了错误,我将在接下来处理这个错误。

    my-common-module中的测试包重命名为com.example.common.test,以便在编译实现测试类时加载测试类。这纠正了在导出与模块中包名相同的测试类时引入的类加载问题,其中第一个jar(在本例中为模块)被加载,而第二个jar(测试jar文件)被忽略。非常有趣的是,根据观察,我得出结论,模块路径比类路径具有更高的优先级,因为Maven编译参数显示tests.jar是在类路径中首先指定的。运行mvn clean validate test-x,我们看到编译html" target="_blank">参数:

    -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
    

    我们需要使导出的测试类可用于实现模块。将此依赖项添加到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>
    

    最后,在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 { ... }
    

    以下是我的MVN clean Package新更改的测试结果:

    我在Java跨模块测试GitHub repo中更新了示例代码。我唯一挥之不去的问题是,当我将实现模块定义为一个常规的jar项目而不是一个模块时,为什么它能起作用,我相信您也是这样做的。不过,改天我会和你玩的。希望我提供的能解决你的问题。

  •  类似资料:
    • 我有一个多模块Maven项目,其中包含子模块ModuleA和ModuleB等。ModuleA将ModuleB列为依赖项。但是,当仅构建模块A时,Maven不会解决模块B的依赖关系-导致。我想避免构建整个父级以使构建更快。目前我发现让它工作的唯一方法是使用exec插件和脚本,将父项目称为“ModuleB, ModuleA”,这意味着我必须在每次依赖项更改时更改脚本(例如添加ModuleC)。 是否有

    • 我无法使用ArchUnit制定以下测试: 我想确保某个包中的所有类只访问应用程序基包之外或某个子包内的类(“或”而不是“xor”)。 我得到的是: 问题是,或条件应该在onlyAccessClassesthat()中。如果一个类同时具有两种类型的访问权限,上述公式将失败,我希望这两种类型都有效。 我怎样才能实现我想要的?谢谢你在这方面的任何帮助...

    • 应用程序 SlidingMenu(源文件) SlidingMenu-Maps-Support(源文件) 这里有一个链接,看看我的意思。 这就是我得到的错误。 谢谢! 编辑1:不要紧!我又回到月食了!Android Studio还没有为真正的项目开发做好准备。

    • 问题内容: 我有一个简单的节点应用程序,它对github上另一个应用程序具有单一依赖性。使用可以很好地安装依赖项,但是当我尝试在其中安装某些东西时,它说不可用。例如,github应用程序将Mongoose安装为依赖项。我认为该父应用程序可以访问该模块,因为它位于子模块中: 结构看起来像这样: 我是否只需要在父级应用程序中同时包含猫鼬作为依赖项,还是可以通过子级方式访问该模块? 问题答案: 我是否只

    • 当运行命令(如 或 Maven无法解决我的一个模块对另一个模块的依赖关系。 [错误]无法在项目分析器上执行目标-app:无法解析项目的依赖项project_group:a:jar:0.1-snapshot:找不到项目project_group:b:jar:0.1-snapshot 项目结构如下: