Where should I put unit tests when migrating a Java 8 project to Jigsaw
Asked Answered
R

3

16

I am currently testing to migrate a Java 8 application to Java 9 / Jigsaw, using jdk-9+149.

The project has been laid out in standard Maven directory layout, i.e. having src/main/java, src/test/java, etc.

As soon as I add the module-info.java to src/main/java, maven-compiler-plugin fails throwing a NullPointerException. This is because it expects to find a module-info for the test directory, too. So, as far as I can see, the options are:

  • keep test classes in a separate module, which means they can only access exported packages of the main module.
  • move test classes into the main module, together with the sources, which means that the main module needs test dependencies.

Obviously, neither of those options seems to be desirable, so I assume there is a recommended way of testing Jigsaw modules in a Maven project. Unfornately, I could neither find recommendations nor examples.

EDIT: Adding some information that I didn't regard relevant when posting the question (sorry)

Rowena answered 28/12, 2016 at 17:4 Comment(2)
what source and target did you specify as configuration in maven-compiler plugin? Also please share the stack trace for the NPE to be clear over the error faced.Heatstroke
Sorry, added some more information. But basically, the NPE was just the symptom that lead me to the actual question, how I would deal with tests in general.Rowena
K
10

Maven support for Java 9 in general and tests in particular is still under development - many things work but others might not. Without seeing the stack trace for the NPE, it is of course speculation, but I assume you ran into this error.

More generally, the question of how exactly unit tests will work with Jigsaw is still being discussed - even on the Jigsaw mailing list.

Here's my opinion on the matter:

  1. As you note, putting tests into a separate module would mean that only public types in exported packages would be testable, which is definitely not enough. There could be workarounds but those require to either edit the module declaration (source code; module-info.java) or descriptor (byte code, module-info.class) on the fly or add a ton of --add-exports command line flags to the javac and java commands compiling and running the tests. None of these sound particularly fun, especially if you want to do it by hand.

  2. Moving tests into the source tree is a bad idea as well for obvious reasons, not least among them that creating a JAR without tests would then require a lot of fiddling.

  3. Another option is to use the --patch-module option that allows to add class-files or the content of a JAR to an existing module. This way the testCompile step could create a JAR containing the source and test files. Unfortunately, unless it manipulates the module declaration/description as described above, the resulting JAR can not be executed without adding reads edges with java --add-reads for the test dependencies. Still, better than above.

  4. As a kind of last resort, there are ways to have the production JAR be treated like a regular JAR instead of as a module. The easiest would probably be to dump it on the class path together with the test JAR. This way everything works as in Java <9. Unfortunately this will break code that uses features under the assumption that it is inside a named module (e.g. certain kinds of interactions with the reflection API).

Personally, I consider 3. to be the best of the above options. It would require no changes in project layout and only comparatively minor additions to javac and java commands.

Kielce answered 28/12, 2016 at 18:33 Comment(4)
Thanks for the elaborated answer, Nicolai. Good to see the topic being under discussion. I will do some testing with options 3 and 1, I didn't know that it is possible to add exports on the command line.Rowena
Glad I could help. Make sure to report back, be it here, with a post, or a mail to jigsaw-dev. At this point all feedback based on actual experimentation is valuable.Kielce
I definitely will :) btw: Robert's comment below also indicates that option 3 might be the way to goRowena
This is now >2 Years old. Is there an update? For now I put junit on the application modulepath(can add opens into module-info) while having all the tests separate. Which works. But honestly I don't like that. Is there some pointer to merged modul-info in two paths(one with aditional Junit information)?Wanting
G
6

You must at least use maven-compiler-plugin 3.6.0 to have jigsaw support. However, since build +148 the binary structure of a class file has changed, so the plugin cannot extract the modulename as before (the modulename is required to be able to compile the tests). I'm working on a fix for that, but I probably depend on a new version of ASM.

UPDATE: maven-compiler-plugin-3.6.1 has been released, so support for jigsaw is restored.

Gleich answered 28/12, 2016 at 22:43 Comment(5)
Thanks, Robert. I didn't see this as a maven issue (except the NPE not being helpful, of course) - because the test code actually isn't modularized. So even if the code compiles and goes into the unnamed module, testing stuff from the src module won't work as desired. But reading Nicolai's anwer I understand that this is still under discussion.Rowena
FYI: maven.apache.org/plugins/maven-compiler-plugin/xref/org/apache/… shows you that the maven-compiler-plugin is using solution 3 from @Nicolai which has been verified as the correct approach by some developers of the jigsaw teamGleich
I think I had a misunderstanding - after I found the time to have a deeper look into your link, I noticed just now that this is the actual code of v3.6.0 (I thought it was a future implementation yet to be released). So AFAICS (not knowing about Maven internals) it looks like I should be able to run tests (without module descriptor) against my modular sources (with module descriptor), if it wasn't for that NPE. If so, it should be possible to build my own compiler plugin locally to get it working for testing purposes. What would the expected return value of getModuleName be?Rowena
The easiest solution is to use a pre-148 release of JDK9. If you want to patch the plugin, getModuleName() should return the name of the module as defined in src/main/java/module-info.java. I chose to extract the value, otherwise you would have to specify it in the module-info file AND in your pom.xml as configuration of the maven-compiler-plugin.Gleich
Thanks so much, hardcoding the module name works for now, as a (dirty) workaround. I had tried to use an earlier version before, but JDK is reluctant to downgrade :-/Rowena
H
1

As already suggested here, using the updated version of the maven-compiler-plugin:3.7.0 shall help you fix this:-

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
            <source>9</source>
            <target>9</target>
            <compilerArgument>-Xlint:all</compilerArgument>
        </configuration>
     </plugin>
</plugins>

Where in you can place your unit tests under the same directory as it used to be prior to modularisation. Here is a sample project from me which is based out of JDK9 and Maven3+. The project follows the directory structure as depicted in the screenshot:-

enter image description here

And post this all you need to do is execute tests using the command:

mvn test

from the root directory of the project to find the tests would be executed as usual.

Heatstroke answered 12/10, 2017 at 4:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.