Search code examples
javamockitojava-17

Mockito can not mock Random in Java 17


Trying to update my project from Java 11 to Java 17 and got an unexpected error from Mockito in a specific test.

mock(java.util.Random.class);

Throws

Feb 04, 2022 3:07:01 PM com.google.inject.internal.MessageProcessor visit
INFO: An exception was caught and reported. Message: java.lang.IllegalAccessException: class 
    net.bytebuddy.description.annotation.AnnotationDescription$ForLoadedAnnotation cannot access interface
    jdk.internal.util.random.RandomSupport$RandomGeneratorProperties (in module java.base) 
    because module java.base does not export jdk.internal.util.random to unnamed module @2f54a33d
org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class java.util.Random.

Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.


Java               : 17
JVM vendor name    : Oracle Corporation
JVM vendor version : 17.0.2+8-86
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 17.0.2+8-86
JVM info           : mixed mode, sharing
OS name            : Mac OS X
OS version         : 12.1

Not sure why Mockito is failing on this test.


Solution

  • The issue here is mockito (via ByteBuddy) is trying to use an inaccessible type at runtime (via reflection). From Java 9 onwards, not all modules are accessible unless you explicitly export/open them.

    As this is a runtime issue, you can add --add-opens as a JVM arg/CLI option to make this type accessible.

    As per the Oracle guide here, --add-opens does the following.

    If you have to allow code on the classpath to do deep reflection to access nonpublic members, then use the --add-opens runtime option.

    If you want to export internal types available in compile time as well, you can use --add-exports.

    To solve your specific issue; use the following.

    --add-opens java.base/jdk.internal.util.random=ALL-UNNAMED.

    ALL-UNNAMED means, a specified package is available in the entire codebase.

    However, mocking types that don't belong to you is not a good practice. Maybe, you can simplify this if there's an alternative.