代码之家  ›  专栏  ›  技术社区  ›  Harald K

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

  •  18
  • Harald K  · 技术社区  · 7 年前

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

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

    然而,我偶然发现了一个我无法解决的问题,并且没有找到任何关于如何解决的描述:

    我怎么能有模块间的 测试 与Maven和Java模块的依赖关系?

    在我的例子中,我有一个“公共”Maven模块,其中包含一些接口和/或抽象类(但没有具体的实现)。在同一个Maven模块中,我有 摘要 测试以确保实现这些接口/抽象类的正确行为。然后,有一个或多个子模块,其中包含接口/抽象类的实现和扩展抽象测试的测试。

    但是,当尝试执行 test maven构建阶段,子模块将失败:

    [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做了一些“魔术”来让测试在模块的范围内执行,它也不适用于我所依赖的模块中的测试(出于某种原因)。我该怎么解决这个问题?

    项目的结构如下( 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
    

    中的依赖项 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>
    

    注意:上面只是我创建的一个项目来演示这个问题。真正的项目要复杂得多,而且 found here (主分支是 但是原理是一样的。

    PS:我不认为代码本身有任何错误,因为所有的东西都是用普通类路径编译的(如在ItLiLJ中,或者是没有Java模块描述符的Maven)。用Java模块和模块路径介绍了该问题。

    1 回复  |  直到 7 年前
        1
  •  6
  •   Jose Henriquez    7 年前

    基于您的演示项目,我可以复制您的错误。也就是说,以下是我在第一次尝试失败后所做的修改,以便能够构建项目:

    1. 我将 maven compiler plugin version 3.8.0添加到所有模块中。您需要3.7或更高版本来用maven编译模块——至少这是netbeans显示的警告。由于没有危害,我将插件添加到 common和 implementation的pom文件中:

      插件 <groupid>org.apache.maven.plugins</groupid> <artifactid>Maven编译器插件</artifactid> <版本>3.8.0</version> <执行> <执行> 目标; <goal>编译</goal> </目标> <id>编译</id> </执行> </执行> & Lt//Plutin & Gt;
      
              
    2. 我将测试类导出到它们自己的 jar file中,这样它们就可以用于您的实现模块或任何人。为此,您需要将以下内容添加到您的 my common module/pom.xml file:。

      插件 <groupid>org.apache.maven.plugins</groupid> <artifactid>maven jar插件</artifactid> <版本>3.1.0</version> <执行> <执行> <id>测试jar</id> <阶段>包</阶段> <目标> <goal>测试jar</goal> &目标; </执行> </执行> & Lt//Plutin & Gt; < /代码>

      这将把 my common module test classes导出到 -tests.jar file-i.e. my-common-module-1.0-snapshot-tests.jar 。注意,没有必要为常规的 jar 文件添加执行,如本文中所述 post. 。然而,这将引入我将在下一步解决的错误。

    3. 将测试包重命名为 my common module to com.example.common.test in,以便在编译实现测试类时加载测试类。这纠正了当我们导出具有与模块中相同包名称的测试类时引入的类加载问题,在这种情况下,加载模块,忽略第二个测试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/4.12/junit-4.12.jar:/homE/TEMSTV//Org/HAMCSTRES/HAMCSTRESARK/1.3/HAMCREST CORE 1.3.JAR:-Mult/TestNV/NETBEANS项目/ MavePosi/Realth/Task/类:/home /TEXTV//My/ReavySt/COM/实例/声明/ 1.0SNAPHOT/SURATION-1.0 SnAPHOSHT.JAR:-SoopCase/HOST/TESTNV/NETBEANS项目/ MavePosi/Stase/SrC/Test/Java:home/testenv/netbeansprojects/mavenproject/implementation/target/generated test source s/test annotations:-s/home/testenv/netbeansprojects/mavenproject/implementation/target/generated test source s/test annotations-g-nowarn-target 11-source 11-encoding utf-8-补丁模块示例。implementation=/home/testenv/netbeansprojects/mavenproject/impLeMeNe/TestNave/NETBeNSsStudio/MaveStudio/Actudio/Src/Test/Java:/HON/TESTNV/NETBEANS项目/ MavePay/Actudi/目标/生成的测试源/测试注释:-AddioRead示例
      
    4. 我们需要将导出的测试类提供给实现模块。将此依赖项添加到您的 my impl module/pom.xml

      依赖关系 <groupid>com。示例</groupid> <artifactid>声明</artifactid> <version>1.0-快照</version> <type>测试jar</type> <scope>测试</scope> </dependency>
      
                       
    5. 最后,在 my impl module test class中,更新导入以指定新的测试包, com.example.common.text ,以访问 my common module test classes:。

      import com.example.declaration.test.abstractfootst;
      导入com.example.declaration.foo;
      导入org.junit.test;
      导入静态org.junit.assert.*;
      
      /**
      *从公共模块继承的测试类…
      */
      公共类fooimplementTest扩展了abstractfootst…}
      

    以下是新更改的my mvn clean package 的测试结果:

    < >我更新了我的样本代码在我的< HRFF=“HTTPS://GITHUBCOM/JANRIQIZZ418/JAVA交叉模块测试”Re= =“NoeFreRever”> Java交叉模块测试 GITHUB RePO。我唯一还没有回答的问题是,我确信您也这么做了,当我将实现模块定义为常规的而不是模块时,它为什么会起作用。不过,改天我会和你一起玩的。希望我提供的解决方案能够解决您的问题。

    t尝试失败,无法生成项目:

    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> 
      
    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>
      

      这将导出 my-common-module 测试类到 -tests.jar 文件-即 my-common-module-1.0-SNAPSHOT-tests.jar . 注意,不需要为常规 罐子 如本文件所述 post . 然而,这将引入我将在下一步解决的错误。

    3. 在中重命名测试包 我的通用模块 com.example.common.test 以便在编译实现测试类时加载测试类。这修正了当我们使用与第一个测试类相同的包名称导出测试类时引入的类加载问题。 罐子 在这种情况下,模块被加载,第二个 罐子 测试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
      
    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>
      
    5. 最后在 my-impl-module 测试类,更新导入以指定新的测试包, com.example.common.text 访问 我的通用模块 测试类:

      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 新的更改:

    enter image description here

    我更新了我的示例代码 java-cross-module-testing 吉瑟布·雷波。我唯一还没有解决的问题是,我相信您也这么做了,当我将实现模块定义为一个常规的 罐子 项目而不是模块。不过,改天我会和你一起玩的。希望我提供的解决了你的问题。