NoSuchBeanDefinitionException for mocked beans dependencies
I am trying to use mocks in my integration test and am not having much luck. I am using Spring 3.1.1 and Mockito 1.9.0 and the situation is like this:
@Component
public class ClassToTest {
@Resource
private Dependency dependency;
}
and
@Component
public class Dependency {
@Resource
private NestedDependency nestedDependency;
}
Now I want to perform an integration test ClassToTest using Spring JavaConfig. This is what I tried and it doesn't work:
@Test
@ContextConfiguration
public class ClassToTestIntegrationTest {
@Resource
private ClassToTest classToTest;
@Resource
private Dependency mockDependency;
@Test
public void someTest() {
verify(mockDependency).doStuff();
// other Mockito magic...
}
@Configuration
static class Config {
@Bean
public ClassToTest classToTest() {
return new ClassToTest();
}
@Bean
public Dependency dependency() {
return Mockito.mock(Dependency.class);
}
}
}
I have simplified the setup to make the question easier to understand. Actually I have more dependencies and I only want to mock some of them - others are real, based on the configuration imported from my prod @Configuration classes.
As a result, I get a NoSuchBeanDefinitionException saying there are no beans of type NestedDependency in the application context. I don't get it - I thought Spring would get Mockito, a mocking Dependency instance, and wouldn't even look at its autodevice. Since that doesn't work, I have to mock my whole object graph which completely hits the point of mockery!
Thanks in advance for your help!
source to share
What Mockito does when mocking classes is it creates a subclass using cglibwith some fancy name: Dependency$EnhancerByMockito
(IIRC). As you probably know, subclasses inherit fields from their parent:
@Component
public class Dependency {
@Resource
private NestedDependency nestedDependency;
}
public class Dependency$EnhancerByMockito extends Dependency{
//...
}
This means that Spring can still see the field in the base class when the layout is presented. What can you do:
-
Use interfaces that will force Mockito to use dynamic proxies rather than CGLIB classes
-
Mock
NestedDependency
- I know it just cascades the problem one level further -
Disable
@Resource
annotation scanning for tests
source to share
I had the same problem and found a different solution. When Spring instantiates all of your beans, it checks if it is a Mockito Mock, in which case I return false for the injection property. To use it, just put it in your Spring context
Code below:
public class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
private static final MockUtil mockUtil = new MockUtil();
public MockBeanFactory() {
super();
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockUtil.isMock(bean);
}
}
source to share