When adding the org.mockito:mockito-inline dependency to the project some tests get stuck.
It happens when we run a single test class, via IDE or CLI, and we have a static variable inside an inner class, like so:
public class MyClassTestApplicationContext {
@Test
public void dontStuck() {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(StuckConfig.myOtherClass.getClass());
beanDefinition.setSource(StuckConfig.myOtherClass);
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition("myOtherClass", beanDefinition);
context.refresh();
}
@Test
public void dontStuckAlso() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(DontStuckConfig.class);
context.refresh();
context.getBean(MyOtherClass.class);
}
@Test
public void stuck() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(StuckConfig.class);
context.refresh();
}
@Configuration
public static class StuckConfig {
private static final MyOtherClass myOtherClass = Mockito.mock(MyOtherClass.class);
@Bean
public MyOtherClass myOtherClass() {
return myOtherClass;
}
}
@Configuration
public static class DontStuckConfig {
private final MyOtherClass myOtherClass = Mockito.mock(MyOtherClass.class);
@Bean
public MyOtherClass myOtherClass() {
return myOtherClass;
}
}
}
How to reproduce: Run the MyClassTestApplicationContext tests in the sample.
You can make it work by doing one of the following:
- Remove the mockito-inline dependency;
- Remove the static modifier from the mock
- Mock an arbitrary object inside the parent class, like so:
public class MyClassTestApplicationContext {
private static final Object obj = Mockito.mock(Object.class);
//... remaining code
Sample: https://github.com/marcusdacoregio/mockito-inline-stuck-bug
There's a minimum setup in the sample to simulate the behavior on the MyClassTestApplicationContext.
Additional information: I could see that the Thread gets stuck at this part of the ByteBuddyAgent. I first found this problem when running a test class in the Spring Security project, which worked before this change.
Affects: 5.3.8
Comment From: sbrannen
At a glance, this appears to be an issue with either Mockito, ByteBuddy, or a combination of the two.
@marcusdacoregio, what makes you think this has something to do with the core Spring Framework?
Comment From: marcusdacoregio
Thanks for the response @sbrannen. The reason to open the issue within the core framework is because I couldn't simulate the problem without using it. So I thought it would be good to ask help from you.
Comment From: sbrannen
If you annotate StuckConfig with @Configuration(proxyBeanMethods = false), your test will pass.
That ensures that a dynamic subclass of StuckConfig is not created using CGLIB.
I didn't dig into the details, but it appears that mockito-inline is not able to create a mock for the static field when it's inside a subclass generated by CGLIB.
In light of that, I don't consider this an issue for the core Spring Framework.
Please note that mockito-inline is an experimental feature. So please open an issue with the Mockito team to see if they can support such use cases.
Comment From: sbrannen
As a side note, I typically try to avoid static fields in @Configuration classes solely for the purpose of testing. There are usually other options for achieving the same goal within your tests.
Comment From: marcusdacoregio
Thank you @sbrannen. I'll talk with the mockito team about this.