'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:
If
maincode is a module then thetestcode also needs to be a module.Each module must have a distinct module name.
- So the
mainandtestcode lives in different modules In the examplecom.foo.implandcom.foo.test
- So the
A single java package may not exist in two modules.
- So the
mainandtestcode cannot share a package. - In the example
com.foo.implandcom.foo.implt.
- So the
The
package privatetrick no longer works because that would need the same package to exist in both mymainand mytestmodules.Which forces me to open the internals I want to test.
- So I either make it fully public (which I do not want).
- 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 |
