spring版 :3.2.4.RELEASE和3.2.9.RELEASE
Mockito版本 :1.8.5
我一直在尝试将H2测试引入一个用于集成测试的旧项目,并且遇到了一些问题。由于事务传播的方式,我需要模拟一个自动装配的类。我以前已经做过,但是现在遇到了严重的问题。初始化测试时将引发以下错误消息:
org.springframework.beans.factory.BeanCreationException:创建名称为’com.stuff.XMLITCase’的bean时出错:注入资源依赖项失败;嵌套的异常是org.springframework.beans.factory.BeanNotOfRequiredTypeException: 名为“ TheProcessor”的Bean必须为[com.stuff.XMLBatchFileProcessor]类型,但实际上 在org.springframework.context.annotation.CommonAnnotationBeanPostProcessor 类型为[$ Proxy118] 。 postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:307)
深入探讨一下,事实证明该bean实际上是代理。如果检查AbstractBeanFactory(第239行),则可以看到代理:
sharedInstance = {$ Proxy117 @ 7035}“ com.stuff.XMLBatchFileProcessor@66c540d0” h =
唯一的问题是,我不知道这是从哪里来的。我已经遍历了配置和依赖项,并且找不到任何应该发生的地方。
不幸的是,我无法为此提供示例项目,但是我将介绍我的测试配置。我有一个扩展用于测试的根类:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:/spring/spring-test-context.xml"}) @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) public abstract class AbstractIntegrationTest { }
这只是加载了一些spring配置,并在每次测试后回滚事务。
尽管我的其他模块与该模块之间有一个区别,但spring config也并不奇怪。这是事务管理器和会话工厂:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="hibernateSessionFactory"/> </bean> <bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> ... </bean>
在我的其他模块中,我使用的是entityManagerFactory和其他事务管理器:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> ... </bean>
实际上,类具有一些自动装配的字段,以及通常的@Service批注:
@Service(value = "TheProcessor") public final class XMLBatchFileProcessor extends BatchFileProcessor implements IXMLBatchProcessor {
最后,实际测试如下:
public class XMLITCase extends AbstractIntegrationTest { @Resource(name = "TheProcessor") @InjectMocks private XMLBatchFileProcessor xmlProcessor; @Mock private ProcessHelper processHelper; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); } @Test public void test() throws Exception { Assert.assertNotNull(xmlProcessor); } }
如果我将XMLBatchFileProcessor替换为该接口并自动将该字段连接起来,那么编译就不会有任何问题。但是,Mockito永远不会用模拟对象替换自动装配的Bean。如果确实如此,那么我将不必理会@Resource批注和命名服务,从而避免了代理问题。
任何帮助,将不胜感激。我将重点讨论会话工厂及其之间的差异,但是我很可能会完全遗漏其他东西。
继续Sotirios的评论,今天早上我又看了一眼,确实错过了xmlProcessor有@Transactional注释,因此这意味着需要代理该类。如果我删除该final声明并让CGLib对其进行增强,那么Mockito会在initMocks(this)调用时替换该bean 。但是,当调用方法时,CGLib似乎用Spring增强版本替换了所有bean,因此覆盖了Mockito版本。
@Transactional
final
initMocks(this)
在带有@Transactional注释的类的集成测试中,同时使用Mockito和Spring的正确方法是什么?
好吧,一旦我意识到由于有@Transactional注释而要对课程进行代理,问题的解决方案就变得更加清晰了。我需要做的是拆开代理,然后直接在其上设置模拟对象:
所以在我的AbstractIntegrationTest:
AbstractIntegrationTest
/** * Checks if the given object is a proxy, and unwraps it if it is. * * @param bean The object to check * @return The unwrapped object that was proxied, else the object * @throws Exception */ public final Object unwrapProxy(Object bean) throws Exception { if (AopUtils.isAopProxy(bean) && bean instanceof Advised) { Advised advised = (Advised) bean; bean = advised.getTargetSource().getTarget(); } return bean; }
然后在我的@Before:
@Before
@Mock private ProcessHelper processHelper; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); IXMLBatchProcessor iXMLBatchProcessor = (IXMLBatchProcessor) unwrapProxy(xmlProcessor); ReflectionTestUtils.setField(iXMLBatchProcessor , "processHelper", processHelper); }
@Autowired在注入正确的模拟对象的同时,所有类都保持完整。
@Autowired