Inject mock into bean failed
For my unit tests, I want to use mocks for some autowired dependencies of the bean under test. The mock is created and injected into the unit test class properly, but injecting it into the bean under test failed with
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No matching bean of type [Service] found for
dependency: expected at least 1 bean which qualifies as autowire candidate for this
dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}
This is my used testContext.xml
<bean id="Service" class="org.easymock.EasyMock" factory-method="createMock">
<constructor-arg value="Service" ></constructor-arg>
</bean>
<bean id="Controller" class="Controller">
<property name="Service" ref="Service"></property>
</bean>
This is the part of the bean under test (Controller.java) where the wirin开发者_运维问答g went fail
@Autowired
private Service service;
But if I autowire the same mock into my unit test class (ControllerTest.java), no error occured,
@Autowired
private Service service;
The injected object is of type
($Proxy18) EasyMock for interface Service
Any suggestion why the injection into the bean under test isn't possible but the injection into the test works ?
Kind regards Dominik
I think the issue might have to do with the order of things. In ControllerTest, I'm guessing you are extending AbstractJUnit4SpringContextTests or something similar that is wiring up all of your beans first (before you even have a chance to create your mock). It's hard to say without seeing your whole class (or at least the class declaration, setUp, tearDown and particular test method).
The usual thing for unit tests (as opposed to integration tests) is to not use spring to wire anything up at all - just use the property setters to inject your mock collaborators manually. Integration tests don't normally use mocks - they use the real objects.
Occasionally, you want to do integration testing, but you want to mock one particular collaborator because the real one does something destructive or sends an SMS message or something. What I have done in those situations is to let Spring wire up everything the way it wants to and then manually inject the mock using the property setter in the class after everything is all initialized. Unless the real class that you want to mock does something in its initialization that you really don't want, there's no problem.
Possibly this has to do with a random effect: Spring's bean instantiation order.
Looking at your application context configuration fragment, spring has no way to know that it has to create the mock object of type Service first because it does not know that the instantiation of the bean with id Service will result in type Service. This may prevent autowiring if the Controller bean is created first.
To confirm this, try
<bean id="Controller" class="Controller" depends-on="Service">
(EDIT: You use autowiring AND an explicit property in the xml config for Service. What happens if you delete the @Autowired from Controller.Service?)
On a side note referring to @jhericks, we sometimes inject mock objects in integrations tests (in the rare cases where this is absolutely neccessary) this way: The application has an optional parameter (command line, config file or so) where we can supply it with an additional xml application context configuration document. This short document overrides the bean declaration for the bean in question, replacing it with the mock variant.
精彩评论