我想将Mockito模拟对象注入到Spring (3+) bean中,以便使用JUnit进行单元测试。我的bean依赖项目前是通过在私有成员字段上使用@Autowired注释注入的。

我考虑过使用ReflectionTestUtils。setField,但是我希望注入的bean实例实际上是一个代理,因此没有声明目标类的私有成员字段。我不希望为依赖项创建公共setter,因为我将纯粹为了测试目的而修改我的接口。

我遵循了Spring社区提供的一些建议,但模拟没有被创建,自动连接失败:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>

我目前遇到的错误如下:

...
Caused by: org...NoSuchBeanDefinitionException:
    No matching bean of type [com.package.Dao] found for dependency:
    expected at least 1 bean which qualifies as autowire candidate for this dependency.
    Dependency annotations: {
        @org...Autowired(required=true),
        @org...Qualifier(value=dao)
    }
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.java:770)

如果我将constructor-arg值设置为无效的值,那么在启动应用程序上下文时就不会出现错误。


当前回答

从Spring 3.2开始,这不再是一个问题。Spring现在支持通用工厂方法结果的自动装配。请参阅本博客文章中“通用工厂方法”一节:http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/。

重点是:

在Spring 3.2中,工厂方法的泛型返回类型现在是 正确地推断,并按类型自动装配模拟应该工作为 预期。因此,自定义变通方法,如 MockitoFactoryBean、EasyMockFactoryBean或springckito可能不是 不再必要。

这意味着这应该是开箱即用的:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>

其他回答

基于上述方法,我将给出几个例子

春天:

@ContextConfiguration(locations = { "classpath:context.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService;
    @Mock
    private TestService2 testService2;
}

没有春天:

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService = new TestServiceImpl();
    @Mock
    private TestService2 testService2;
}

我找到了一个与teabot类似的答案,以创建一个提供模拟的MockFactory。我使用下面的例子来创建模拟工厂(因为到narkisr的链接是死的): http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/org/randompage/bookmarking/backend/testUtils/MocksFactory.java

<bean id="someFacade" class="nl.package.test.MockFactory">
    <property name="type" value="nl.package.someFacade"/>
</bean>

这也有助于防止Spring想要解析来自模拟bean的注入。

也许不是完美的解决方案,但我倾向于不使用spring来进行单元测试的DI。单个bean(被测试的类)的依赖关系通常不会太复杂,所以我只是直接在测试代码中进行注入。

考虑到:

@Service
public class MyService {
    @Autowired
    private MyDAO myDAO;

    // etc
}

您可以通过自动装配加载被测试的类,使用Mockito模拟依赖项,然后使用Spring的ReflectionTestUtils将模拟注入到被测试的类中。

@ContextConfiguration(classes = { MvcConfiguration.class })
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
    @Autowired
    private MyService myService;

    private MyDAO myDAOMock;

    @Before
    public void before() {
        myDAOMock = Mockito.mock(MyDAO.class);
        ReflectionTestUtils.setField(myService, "myDAO", myDAOMock);
    }

    // etc
}

请注意,在Spring 4.3.1之前,此方法不适用于代理后面的服务(例如用@Transactional或Cacheable注释)。sprr -14050已经修复了这个问题。

对于早期版本,一种解决方案是打开代理,如文中所述:事务性注释避免模拟服务(这就是ReflectionTestUtils。setField现在默认执行)

看看springingckito的开发速度和开放问题的数量,我现在有点担心把它引入我的测试套件堆栈。事实上,上一个版本是在Spring 4发布之前发布的,这带来了一些问题,比如“是否可以轻松地将它与Spring 4集成?”我不知道,因为我没试过。如果我需要在集成测试中模拟Spring bean,我更喜欢纯Spring方法。

有一个选项可以使用简单的Spring特性来伪造Spring bean。你需要使用@Primary, @Profile和@ActiveProfiles注解。我就这个话题写了一篇博客。