Recently I started a new Spring Boot app using version 2.0.5 and I used Java 11. Everything seemed like no big deal at first and I got through some of the bootstrapping phases of getting a walking skeleton going, building the Docker container with Maven, and even pushing the snapshot version and its Docker container out to AWS ECR.
Then I wrote the first controller, and, unexpectedly, I couldn’t write an integration test (@WebMvcTest) for it. When I tried, I got the following error:
[ERROR] testGetGear(org.gearbuddy.GearControllerTest) Time elapsed: 0 s < << ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: interface org.gearbuddy.data.GearRepository.
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 : 11
JVM vendor name : Oracle Corporation
JVM vendor version : 11.0.2+9-Ubuntu-3ubuntu118.04.3
JVM name : OpenJDK 64-Bit Server VM
JVM version : 11.0.2+9-Ubuntu-3ubuntu118.04.3
JVM info : mixed mode, sharing
OS name : Linux
OS version : 4.15.0-48-generic
Underlying exception : java.lang.UnsupportedOperationException: Cannot define class using reflection
Caused by: java.lang.UnsupportedOperationException: Cannot define class using reflection
Caused by: java.lang.IllegalStateException: Could not find sun.misc.Unsafe
Caused by: java.lang.NoSuchMethodException: sun.misc.Unsafe.defineClass(java.lang.String, [B, int, int, java.lang.ClassLoader, java.security.ProtectionDomain)
Huh, that's weird!
This was the code it couldn't mock:
public interface GearRepository extends CrudRepository {
}
Literally nothing special!
How did I fix it?
The Solution
The solution was just to explicitly add a newer version of Mockito to my POM:
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.22.0</version>
<scope>test</scope>
</dependency>
Before this, I had Mockito 2.15.0, which came in as a transitive dependency through spring-boot-starter-test.
So why does adding the explicit Mockito version work?
Well, the problem is that MockBean in the version of Mockito imported as a transitive dependency (I think from Spring Boot parent pom (?) but I didn't check) used stuff from the sun.misc.unsafe package. Which was removed in Java 9.
The unfortunate thing was that it took me hours to figure out that's what was going on. Because if you start googling around for "Mockito cannot mock this class CrudRepository" you'll hit a lot of articles about how Spring Boot (particularly in regards to the @WebMvcTest annotation) creates the application context and when beans are available and what name they have when they're made available and all that.
On the other hand, if you look up something like "Mockito java 11 sun.misc.unsafe" (which you'll eventually try once you read all the way through to the end of the above stack trace) you'll come across this very helpful Stack Overflow answer.
The new faster upgrade cycle for Java is likely to introduce some dependency issues like this one to your applications. Thankfully the Java community online is a strong and helpful one. With a little careful reading of any error output, you will have no problems finding the solutions you need.
Top comments (6)
Thanks for this, it saved me countless hours down a rabbit hole. Now I can remain unencumbered by the thought process!
@ben thanks for making this amazing platform where an article I wrote three years ago is still helping people today! you and your team rock!
Thanks, this post help me
Thanks, for the right solution
thanks, i was trying to fix it for 2 hours.
Hi
Thanks for the post adding mockito the test project pom.xml fixed it
rama anne