如何打开包并要求仅针对 JUnit 测试的测试范围模块的依赖项

我正在使用classpath将一个jar项目从java 10迁移到使用java 9拼图模块的java 11。该项目有 JUnit5 测试。测试依赖项由 maven 在测试范围内提供。如何使所有包都打开以供测试,但在模块被另一个项目使用时不打开?

jar项目只是为其他项目提供了几个类(如实用程序项目)(因此不需要主类)。

该项目在 /src/main/java/a/b/c/ 上有 5 个包。其中 2 个应该可供使用此 jar 的项目访问。其他3个仅供内部使用(由可访问的供访问者使用)。测试位于 /src/test/java/a/b/c/。这些测试在测试范围内提供了依赖项(JUnit,mockito,junt-params),因为这些测试与使用此jar的项目无关

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>

我在 /src/main/java/ 上提供了一个 module-info.java

module moduleName {

requires java.base;

exports a.b.c.package1;
exports a.b.c.package3;
}

所以现在包1和3中的公共类应该可以像预期的那样被其他项目访问(我还没有能够验证这一点)。

运行测试现在会导致 java.lang.reflect.InaccessibleObjectException: 无法使 a.b.c.package1.collections.SomeTest() 可访问:module moduleName 不会“打开 a.b.c.package1”到未命名的模块@6a84a97d

当我打开包(打开)时一切都运行顺利。但现在所有软件包现在都已打开。我希望包 2、4 和 5 只能在测试时访问,而 1 和 3 不应该打开进行反射(因此只能导出)。

我开始思考,由于maven告诉我未命名的模块@6a84a97d,这可能是一个为测试而创建的模块。这让我尝试在 /src/test/java/ 上添加一个 module-info.java 进行测试。

module moduleNameTest {
requires moduleName; // the code to test
requires java.base;  // java.base could be transient on moduleName

// test dependencies
requires org.junit.jupiter.api;
requires org.junit.jupiter.params;
}

现在 maven (3.5.4) 指出:

src/test/java/module-info.java:[3,20] module not found: moduleName
src/test/java/module-info.java:[4,31] module not found: org.junit.jupiter.api
src/test/java/module-info.java:[5,31] module not found: org.junit.jupiter.params

技术:

  • java openJDK 11
  • mvn 3.5.4
  • JUnit 5.3.0
  • Surefire 插件 3.0.0-M3
  • mockito 2.21.0

如前所述,我希望包 2、4 和 5 只能在测试时访问,并且测试将在使用 maven 构建 jar 时运行。包 1 和包 3 应导出以在其他项目中使用,但不能打开以进行反射(因此仅导出而不打开)。

如果您需要更多信息,请随时询问。

提前致谢

凯文


答案 1

“欢迎来到模块化世界中的测试”,Kevin。

我在这里写了一个关于这个主题的博客:https://github.com/sormuras/testing-in-the-modular-world

基本上,当涉及到白盒测试时,您需要在测试编译测试运行时调整模块系统,以允许测试框架绕过模块系统的障碍。

我想,你走在正确的轨道上...也许Surefire做错了事情?想给我 https://github.com/sormuras/junit-platform-maven-plugin 写一个镜头吗?此插件支持开箱即用的黑盒和白盒测试。特别是,当您提供测试模块描述符时,此插件会大放异彩。test/java/module-info.java

请参阅此“图片”,了解如何在不接触主模块描述符的情况下组织模块化测试:

src ├── main │ └── java │ ├── foo │ │ ├── PackageFoo.java │ │ └── PublicFoo.java │ └── module-info.java <------------------ module foo { exports foo; } ├── test │ └── java .--- open module foo { │ ├── foo / exports foo; │ │ └── PackageFooTests.java / requires org.junit.jupiter.api; │ └── module-info.[java|test] <----< } └── it \ └── bar °---- --add-reads └── src foo=org.junit.jupiter.api └── test --add-opens └── java foo/foo=org.junit.platform.commons ├── bar │ └── PublicFooTests.java └── module-info.java <------ open module bar { requires foo; requires org.junit.jupiter.api; }

此模式也应该易于采用到您的设置中。

相关问题:如何在模块化Java项目中组织测试?


答案 2

如果 junit 被打包为 java9 模块,这个问题本可以得到解决。比你的错误会变成类似

“无法使 a.b.c.package1.collections.SomeTest() 可访问:module moduleName 不会”打开 a.b.c.package1“到 junit5 模块@6a84a97d

在这种情况下,你可以打开它到 junit5 模块(或任何“命名”测试模块,它将尝试在你的类上使用反射)。由于目前我认为情况并非如此(从java的角度来看,junit5属于未命名模块),因此您的替代选项将是

  1. 将测试移动到单独的 maven 模块。您将有两个 maven 模块和 . 将依赖于 ,所以在 maven 中它会构建。然后,您可以在 .您的主模块仍将受到保护。yourModule-1.0yourModule-tests-1.0yourModule-tests-1.0yourModule-1.0open module moduleNameTest {}module-info.java

  2. 带有一些“前缀包”的测试的前缀包,因此您将拥有 .然后,当您仅打开测试包进行反射,同时保持主要来源安全时,因为它保留在另一个包中a.b.c.package1.collections.SomeTest()a.b.c.mytests.package1.collections.SomeTest()a.b.c.mytests.package1.collections.SomeTest()

  3. 您现在可以忍受这一点,只需使用打开您的软件包即可。除非你处理一些共享库,否则可能没有人会想在你的代码上使用反射。opens a.b.c.package1.collections.SomeTest()

  4. 遵循第3点的假设。最简单的方法是打开整个模块进行反射,方法是将其声明为而不仅仅是'。这样,您就无需在每次在尚未打开的包中创建测试时进行调整。open module moduleName {}module moduleName {}module-info.java


推荐