javamockitojava-module

Mockito and module-info with Version 5.16.0+: Mockito cannot mock this class


I'm using the Java Module System since some years now and until Mockito 5.15.2 this was no Problem. But with Version 5.16.0+ I now got the following error:

Mockito cannot mock this class: class javax.xml.parsers.DocumentBuilder

Because of this I tried to add something like the following to the test module-info:

opens java.xml to org.mockito;

without luck. So within the release notes of Mockito 5.16.0 I found:

Add support for including module-info in Mockito.

Which is great/nice, but an explanation what to change would be nice. But maybe my module-info:

requires org.mockito;

is wrong, because mockito seems to export every package like org.mockito.internal.* which looks strange to me.

So my question is how to correctly make my tests work again with mockito Versions 5.16.0+?

Full error message:

[ERROR] Errors:
[ERROR]   AHASessionMiniTests.testNewInstance2:345 Mockito
Mockito cannot mock this class: class javax.xml.parsers.DocumentBuilder.

If you're not sure why you're getting this error, please open an issue on GitHub.


Java               : 17
JVM vendor name    : Oracle Corporation
JVM vendor version : 17.0.8+9-LTS-211
JVM name           : Java HotSpot(TM) 64-Bit Server VM
JVM version        : 17.0.8+9-LTS-211
JVM info           : mixed mode, sharing
OS name            : Windows 10
OS version         : 10.0


You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

Underlying exception : java.lang.IllegalArgumentException: Could not create type

Stacktrace:

Underlying exception : java.lang.IllegalArgumentException: Could not create type
        at de.powerstat.fb.mini/de.powerstat.fb.mini.test.TR64SessionMiniTests.testToString(TR64SessionMiniTests.java:151)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.lang.IllegalArgumentException: Could not create type
        at net.bytebuddy@1.15.11/net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:170)
        at net.bytebuddy@1.15.11/net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:399)
        at net.bytebuddy@1.15.11/net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:190)
        at net.bytebuddy@1.15.11/net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:410)
        at org.mockito/org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:75)
        at org.mockito/org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClass(InlineBytecodeGenerator.java:223)
        at org.mockito/org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.lambda$mockClass$0(TypeCachingBytecodeGenerator.java:78)
        at net.bytebuddy@1.15.11/net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:168)
        at net.bytebuddy@1.15.11/net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:399)
        at net.bytebuddy@1.15.11/net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:190)
        at net.bytebuddy@1.15.11/net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:410)
        at org.mockito/org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:75)
        at org.mockito/org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMockType(InlineDelegateByteBuddyMockMaker.java:429)
        at org.mockito/org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.doCreateMock(InlineDelegateByteBuddyMockMaker.java:388)
        at org.mockito/org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMock(InlineDelegateByteBuddyMockMaker.java:367)
        at org.mockito/org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMock(InlineByteBuddyMockMaker.java:56)
        at org.mockito/org.mockito.internal.util.MockUtil.createMock(MockUtil.java:99)
        at org.mockito/org.mockito.internal.MockitoCore.mock(MockitoCore.java:84)
        at org.mockito/org.mockito.Mockito.mock(Mockito.java:2198)
        at org.mockito/org.mockito.Mockito.mock(Mockito.java:2113)
        ... 4 more
Caused by: java.lang.IllegalAccessError: superclass access check failed: class org.mockito.internal.creation.bytebuddy.codegen.DocumentBuilder$MockitoMock$0UHi1uwe (in module org.mockito) cannot access class javax.xml.parsers.DocumentBuilder (in module java.xml) because module org.mockito does not read module java.xml
        at java.base/java.lang.ClassLoader.defineClass0(Native Method)
        at java.base/java.lang.System$2.defineClass(System.java:2307)
        at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2439)
        at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2416)
        at java.base/java.lang.invoke.MethodHandles$Lookup.defineClass(MethodHandles.java:1843)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at net.bytebuddy.utility.Invoker$Dispatcher.invoke(Unknown Source)
        at net.bytebuddy@1.15.11/net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForNonStaticMethod.invoke(JavaDispatcher.java:1033)
        at net.bytebuddy@1.15.11/net.bytebuddy.utility.dispatcher.JavaDispatcher$ProxiedInvocationHandler.invoke(JavaDispatcher.java:1163)
        at jdk.proxy2/jdk.proxy2.$Proxy86.defineClass(Unknown Source)
        at net.bytebuddy@1.15.11/net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup.injectRaw(ClassInjector.java:1685)
        at net.bytebuddy@1.15.11/net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.injectRaw(ClassInjector.java:166)
        at net.bytebuddy@1.15.11/net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:154)
        at net.bytebuddy@1.15.11/net.bytebuddy.dynamic.loading.ClassLoadingStrategy$UsingLookup.load(ClassLoadingStrategy.java:519)
        at net.bytebuddy@1.15.11/net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:101)
        at net.bytebuddy@1.15.11/net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6424)
        at org.mockito/org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator.mockClass(SubclassBytecodeGenerator.java:282)
        at org.mockito/org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.lambda$mockClass$0(TypeCachingBytecodeGenerator.java:78)
        at net.bytebuddy@1.15.11/net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:168)
        ... 23 more

Solution

  • The cause of the problem is:

    Caused by: java.lang.IllegalAccessError: superclass access check failed: class org.mockito.internal.creation.bytebuddy.codegen.DocumentBuilder$MockitoMock$0UHi1uwe (in module org.mockito) cannot access class javax.xml.parsers.DocumentBuilder (in module java.xml) because module org.mockito does not read module java.xml
    

    This comes from the fact that Mockito is defining the mock class in the org.mockito module. But the mock class inherits from the class being mocked, and that latter class comes from the java.xml module. This causes the IllegalAccessError because the org.mockito module does not reads the java.xml module (because the former does not require the latter, nor does it require any modules which transitively require the latter).

    There's two solutions I can think of:

    1. Don't put Mockito on the module-path (i.e., don't load Mockito as a named module). When Mockito is on the class-path it will be placed in the "unnamed module" which reads all resolved named modules1.

    2. Pass --add-reads org.mockito=java.xml when running your tests. This would be needed in addition to any --add-opens argument you might already be passing.


    A solution that keeps Mockito on the module-path but also avoids the --add-reads argument would need to be implemented by Mockito, if I'm not mistaken. Two potential solutions might be:

    1. Use a MethodHandles.Lookup that defines the mock class in the class-to-mock's module instead of the org.mockito module. Doing that would mean both classes are in the same module, and a module obviously reads itself. The problem with this is that the Lookup would need package access for the class-to-mock's package, which might force --add-opens arguments that were not previously needed.

    2. Mockito could call Module#addReads(Module) on its module for all modules in its ModuleLayer (and any parent layers, recursively). Alternatively, it could call addReads on demand (as mocks are requested).

    I only mention these options in case someone submits a bug report to Mockito and wants to offer potential solutions.


    1. If you were placing the previous versions of Mockito on the module-path, and those versions of Mockito did not include a module-info descriptor yet, then Mockito was an automatic module. That would explain why it worked before. An automatic module, like the unnamed module, reads all resolved named modules.