'Structuring a project with internal tests for Java 9+ modules

I'm trying to learn how the Java 9+ module system works and I ran into an aspect where my old way of structuring a project no longer works.

What I have done in several projects is that for a test to check if the internals of my class were in the right state I created a package private access to get to that state. Then my test class would be in the same package and could access this field/method, yet normal users of my class would not be able to get to this.

Following the example provided in the maven-surefire-plugin documentation for JUnit 5 I find that only reflection remains as an option to get to those internal variables for testing.

To me this all feels like I fell down the wrong rabbit hole because of my current lack of knowledge.

I have not been able to find/figure out what the normal/intended way of doing such a common and normally trivial thing.

How do you do this properly?

What is the correct way to structure a maven based java project so the tests can access the internals of my classes?


Note I published my attempts here https://github.com/nielsbasjes/learnjavamodules

Following the examples and documentation I have found so far I find myself sliding down this slope:

  1. If main code is a module then the test code also needs to be a module.

  2. Each module must have a distinct module name.

    • So the main and test code lives in different modules In the example com.foo.impl and com.foo.test
  3. A single java package may not exist in two modules.

    • So the main and test code cannot share a package.
    • In the example com.foo.impl and com.foo.implt.
  4. The package private trick no longer works because that would need the same package to exist in both my main and my test modules.

  5. Which forces me to open the internals I want to test.

    1. So I either make it fully public (which I do not want).
    2. Or I have to do 'opens' and resort to reflection magic to get to the field.
opens nl.basjes.stats 
   to com.esotericsoftware.kryo,
      tests.nl.basjes.jpms.experiment;

and in the tests

Field sumField = average.getClass().getDeclaredField("sum");
sumField.setAccessible(true);
int sum = (int) sumField.get(average);
assertEquals(10, sum);

intstead of just doing

assertEquals(10, average.sum);


Solution 1:[1]

You can use a module also from code that is not in a module, and currently, JUnit is not modularised. So you write your JUnit tests as usual, with the drawback that you cannot test features that behave different when used by a module. But as these are not internal by definition, this should not harm you much.

For the API tests (the public stuff), you create a new project with a separate module, and wrote your tests there. Currently, I do not JUnit for these tests.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 tquadrat