I have a problem that seems really strange to me. I have the following setup:
An interface:
package com.example;
public interface SomeDependency {
}
A spring component:
package com.example;
@Component
public class SomeClass {
}
A spring test config with a mocked bean generated by EasyMock:
<beans ....>
<context:component-scan base-package="com.example"/>
<bean id="someInterfaceMock" class="org.easymock.EasyMock" factory-method="createMock">
<constructor-arg value="com.example.SomeDependency" />
</bean>
</beans>
And a unit test:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/testconfig.xml")
public class SomeClassTest {
@Autowired
SomeClass someClass;
@Autowired
SomeDependency someDependency;
@Test
public void testSomeClass() throws Exception {
assertNotNull(someClass);
}
@Test
public void testSomeDependency() throws Exception {
assertNotNull(someDependency);
}
}
The project compiles and the tests pass without any problem, i.e. autowiring of both SomeClass (a "real" object) and SomeDependency (a mock object generated by EasyMock) succeed.
However, if I change the implementation of SomeClass to:
@Component
public class SomeClass {
@Autowired
SomeDependency someDependency;
}
both tests fail because
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.example.SomeDependency] 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)}
So my questions are:
- Why does Spring fail to autowire the dependency to SomeClass (when it succeeds autowiring the same dependency to SomeClassTest)?
- How can I change the SomeClassTest or testconf开发者_如何学Cig.xml to make the tests pass?
Comment: In reality the class represented by SomeClass is part of a framework. Consequently, it cannot easily be updated, at least not within reasonable time.
Dependencies:
- Spring: 3.0.5.RELEASE
- EasyMock: 3.0
Edit:
As of Spring 3.2 RC1, the problem with generic factory methods and mock objects has been solved.
/Mattias
It seems the order of the definitions in the xml actually matter when using factories to create beans with autowiring. If you place the declaration of someInterfaceMock
above component-scan
it will work.
Some clarification why: When Spring tries to autowire SomeClass
it searches for a bean of type SomeDependency
. At this stage someInterfaceMock
is still a factory so Spring checks the signature of the factory method EasyMock.createMock(...)
which returns <T>
so Spring only finds an Object
which isn't the type required.
A better way would be to use Spring's FactoryBean
interface to create your mocks.
Here is a basic implementation that should work:
public class EasyMockFactoryBean<T> implements FactoryBean<T> {
private Class<T> mockedClass;
public void setMockedClass(Class mockedClass) {
this.mockedClass = mockedClass;
}
public T getObject() throws Exception {
return EasyMock.createMock(mockedClass);
}
public Class<T> getObjectType() {
return mockedClass;
}
public boolean isSingleton() {
return true;
}
}
Here is the bean definition (the order won't matter!):
<bean class="com.example.EasyMockFactoryBean">
<property name="mockedClass" value="com.example.Dependancy"/>
</bean>
精彩评论