Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java modules: accessibility problems for Mockito 2.20.0

I am migrating from Java 8 to Java 10, and I am running my test which now fails because of package protected classes. The build is run under maven 3.5.4 + Oracle JDK 10.0.2:

  • maven-compiler-plugin 3.7.0 + asm 6.2
  • maven-surefire-plugin 2.22.0 + asm 6.2 + junit 5.2.0
  • asm 6.2 is required for both compiler/surefire because of a bug in the version of ASM used by those plugins.
  • mockito-core 2.20.0 (but was using 2.20.0 with Java 8 before).
  • Eclipse Photon R

The project can be found here ide-bugs.zip (it is located at Eclipse forum because I've made this Topic on Eclipse for another problem, this time with Eclipse having local error with module).

The test is very simple: we try to mock different class, with different access level - all of which were working in Java 8.

  1. package protected class
  2. public class but not exported, not opened
  3. public class not exported but opened to Mockito
  4. public class not exported but opened to all
  5. package protected class not exported but opened to Mockito
  6. package protected class not exported but opened to all

In Java 8, case 1, 5 and 6 are the same (access to package protected). Case 2, 3 and 4 are the same (access to public).

The test fails because Mockito is unable to either:

  • class org.mockito.codegen.NotExportedOpenToMockitoProtected$MockitoMock$117073031 cannot access its superclass nodatafound.mjpmsuc.withopens.NotExportedOpenToMockitoProtected
  • class org.mockito.codegen.NotExportedNotOpenedPublic$MockitoMock$365628885 (in unnamed module @0x3f07b12c) cannot access class nodatafound.mjpmsuc.internal.NotExportedNotOpenedPublic (in module nodatafound.mockito_jpms_usecase) because module nodatafound.mockito_jpms_usecase does not export nodatafound.mjpmsuc.internal to unnamed module @0x3f07b12c

Mockito effectively have a Automatic-Module-Name but is seen as the unamed module because all jar found in the class path for a big "unnamed module".

While I'm fine with migrating from package-protected to non exported package, I fail to understand how I can address the problem keeping my interface/class not visible to other modules ?

[edit] updated the version of plugin/dependency one month after, no result.

like image 789
NoDataFound Avatar asked Mar 11 '26 11:03

NoDataFound


1 Answers

I found part of answer to my problem here: https://blog.codefx.org/java/java-module-system-tutorial/#Open-Packages-And-Modules

  • Mockito is using reflection to access classes from module or class path.
  • Mockito is in the "unnamed module" because Maven adds it into the class path rather than the module path. This explains why the opens package to org.mockito never works: there is no org.mockito module.
  • Maven Surefire does not care to contribute to the "opens" of the module in order to allow Mockito to access it.
  • Mockito is (no longer?) able to mock non-private & non-final classes class. By any means package protected class are private. The error is rather explicit: Mockito create a class extending the package protected class, which now fails (it was working before, but this was probably because Mockito created the class in the same package than the one being mocked).

Nevertheless, this give a problematic configuration in the pom.xml of each module:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
          <argLine>    
  --add-opens nodatafound.mockito_jpms_usecase/nodatafound.mjpmsuc=ALL-UNNAMED
        </configuration>      
      </plugin>

We need to explicitly add opens to an unnamed module. This should not be done in the module-info.java because it expose the module to all other modules or jars which is against encapsulation.

This is problematic because:

  • You need to specify it in the pom.xml for each package.
  • It add additional burdens to the surefire configuration which I prefer simple.
  • You don't have validation from the IDE; Eclipse will validate module-info.java marking invalid package.
  • m2e does not pass to Eclipse JUnit plugin the necessary <argLine /> making the test fail in Eclipse.

The maven approach (which is the same in Eclipse, and perhaps Gradle as far as I know) does not permits an additional module-info for the tests; eg: lets test dependency be modular (this could be probably be done using a dedicated test module per source module like Eclipse does for plugin' tests).

like image 92
NoDataFound Avatar answered Mar 13 '26 03:03

NoDataFound