简体   繁体   中英

How do I open packages and require dependencies on test scope modules only for JUnit testing

I'm migrating a jar project from java 10 using classpath to java 11 using the java 9 jigsaw modules. There are JUnit5 tests for the project. The test dependencies are provided at test scope by maven. How to make all packages open for testing but not open when the module is used by another project?

The jar project is just providing a few classes (like a utility project) for other projects (so no main class needed).

The project got 5 packages at /src/main/java/a/b/c/ . 2 of them should be accessible for projects using this jar. The other 3 are for internal use only (used by the accessible ones). The tests are located at /src/test/java/a/b/c/ . These tests have dependencies (JUnit, mockito, junt-params) provided in test scope, since the tests are not relevant for the projects using this jar

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

I have provided a module-info.java at /src/main/java/ :

module moduleName {

requires java.base;

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

so now the public classes in package 1 and 3 should be accessible to other projects as expected (I have not been able to verify this yet).

Running the Tests now results in java.lang.reflect.InaccessibleObjectException: Unable to make abcpackage1.collections.SomeTest() accessible: module moduleName does not "opens abcpackage1" to unnamed module @6a84a97d

when I open the packages (with opens ) everything runs smooth. But now all packages are now opened. I expect package 2, 4 and 5 to be only accessible while testing and 1 and 3 should not be open for reflection (so only exported).

I came to think, since maven tells me unnamed module @6a84a97d , this might be a module created for testing. That made me try adding a module-info.java at /src/test/java/ for testing.

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

now maven (3.5.4) states:

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

Technologies:

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

As stated earlier I expect package 2, 4 and 5 to be only accessible while testing and the tests to be run on building the jar with maven. Package 1 and 3 should be exported for use in other projects but not open for reflection (so only exported not opened).

If you need additional information don't hesitate to ask.

Thanks in Advance

Kevin

"Welcome to Testing In The Modular World", Kevin.

I compiled a blog about that topic here: https://github.com/sormuras/testing-in-the-modular-world

Basically, when it comes to white-box testing, you need to tweak the module system either at test compile or test runtime to allow testing frameworks to by-pass the module system barriers.

I guess, you're on the right track ... maybe Surefire does the wrong thing? Want to give https://github.com/sormuras/junit-platform-maven-plugin I wrote a shot? This plugin supports black- and white-box testing out of the box. Especially, this plugin shines, when you provide a test/java/module-info.java test module descriptor.

See this "picture" for how to organize modular tests without touching the main module descriptor:

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

This pattern should be easy to adopt to your setup as well.

Related question: How do you organize tests in a modular Java project?

The issue could have been solved if junit was packaged as java9 module. Than your error would change to something like

"Unable to make abcpackage1.collections.SomeTest() accessible: module moduleName does not "opens abcpackage1" to junit5 module @6a84a97d

In this case you could open it just to junit5 module (or any"named" test module which would be trying to use reflection on your classes). Since currently I don't think this is the case (junit5 belongs to unnamed module from java perspective), your alternate options would be

  1. move tests to separate maven module. You would have two maven modules yourModule-1.0 and yourModule-tests-1.0 . yourModule-tests-1.0 would depend on yourModule-1.0 , so in maven it would build. Then you could open only your test module for reflection with open module moduleNameTest {} in your module-info.java . You main module would still be protected.

  2. prefix package with tests with some "prefix-package", so instead abcpackage1.collections.SomeTest() you would have abcmytests.package1.collections.SomeTest() . Then when you open only your tests package abcmytests.package1.collections.SomeTest() for reflection while keeping your main source safe, as it remains in another package

  3. you can live with that for now and simply open your package with opens abcpackage1.collections.SomeTest() . Unless you work on some shared library, noone ever would probably want to use reflection on your code.

  4. Following on assumption from point 3. the easiest is to open the whole module for reflection, by declaring it as open module moduleName {} instead of just ' module moduleName {} . This way you don't need to adjust your module-info.java each time you create test in not-yet-open package.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM