我想将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 Boot 1.4,它有一种很棒的方式来实现这一点。只需要在你的类上使用新品牌@SpringBootTest,在字段上使用@MockBean, SpringBoot就会创建一个这种类型的mock,并将它注入到上下文中(而不是注入原来的那个):
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
@MockBean
private RemoteService remoteService;
@Autowired
private Reverser reverser;
@Test
public void exampleTest() {
// RemoteService has been injected into the reverser bean
given(this.remoteService.someCall()).willReturn("mock");
String reverse = reverser.reverseSomeCall();
assertThat(reverse).isEqualTo("kcom");
}
}
另一方面,如果你没有使用Spring Boot,或者你使用的是以前的版本,你将不得不做更多的工作:
创建一个@Configuration bean,将模拟对象注入Spring上下文:
@Configuration
@Profile("useMocks")
public class MockConfigurer {
@Bean
@Primary
public MyBean myBeanSpy() {
return mock(MyBean.class);
}
}
使用@Primary注释可以告诉spring,如果没有指定限定符,则该bean具有优先级。
确保您用@Profile(“useMocks”)注释了类,以便控制哪些类将使用模拟,哪些类将使用真正的bean。
最后,在你的测试中,激活userMocks配置文件:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
@ActiveProfiles(profiles={"useMocks"})
public class YourIntegrationTestIT {
@Inject
private MyBean myBean; //It will be the mock!
@Test
public void test() {
....
}
}
如果你不想使用mock而是真正的bean,就不要激活useMocks配置文件:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
public class AnotherIntegrationTestIT {
@Inject
private MyBean myBean; //It will be the real implementation!
@Test
public void test() {
....
}
}
如果您正在使用Spring Boot 1.4,它有一种很棒的方式来实现这一点。只需要在你的类上使用新品牌@SpringBootTest,在字段上使用@MockBean, SpringBoot就会创建一个这种类型的mock,并将它注入到上下文中(而不是注入原来的那个):
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
@MockBean
private RemoteService remoteService;
@Autowired
private Reverser reverser;
@Test
public void exampleTest() {
// RemoteService has been injected into the reverser bean
given(this.remoteService.someCall()).willReturn("mock");
String reverse = reverser.reverseSomeCall();
assertThat(reverse).isEqualTo("kcom");
}
}
另一方面,如果你没有使用Spring Boot,或者你使用的是以前的版本,你将不得不做更多的工作:
创建一个@Configuration bean,将模拟对象注入Spring上下文:
@Configuration
@Profile("useMocks")
public class MockConfigurer {
@Bean
@Primary
public MyBean myBeanSpy() {
return mock(MyBean.class);
}
}
使用@Primary注释可以告诉spring,如果没有指定限定符,则该bean具有优先级。
确保您用@Profile(“useMocks”)注释了类,以便控制哪些类将使用模拟,哪些类将使用真正的bean。
最后,在你的测试中,激活userMocks配置文件:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
@ActiveProfiles(profiles={"useMocks"})
public class YourIntegrationTestIT {
@Inject
private MyBean myBean; //It will be the mock!
@Test
public void test() {
....
}
}
如果你不想使用mock而是真正的bean,就不要激活useMocks配置文件:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
public class AnotherIntegrationTestIT {
@Inject
private MyBean myBean; //It will be the real implementation!
@Test
public void test() {
....
}
}