将 Mockito 模拟注入 Spring bean
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2457239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Injecting Mockito mocks into a Spring bean
提问by teabot
I would like to inject a Mockito mock object into a Spring (3+) bean for the purposes of unit testing with JUnit. My bean dependencies are currently injected by using the @Autowiredannotation on private member fields.
为了使用 JUnit 进行单元测试,我想将 Mockito 模拟对象注入 Spring (3+) bean。我的 bean 依赖项目前是通过使用@Autowired私有成员字段上的注释注入的。
I have considered using ReflectionTestUtils.setFieldbut the bean instance that I wish to inject is actually a proxy and hence does not declare the private member fields of the target class. I do not wish to create a public setter to the dependency as I will then be modifying my interface purely for the purposes of testing.
我已经考虑过使用,ReflectionTestUtils.setField但我希望注入的 bean 实例实际上是一个代理,因此没有声明目标类的私有成员字段。我不希望为依赖项创建公共设置器,因为我将纯粹出于测试目的修改我的界面。
I have followed some advicegiven by the Spring community but the mock does not get created and the auto-wiring fails:
我遵循了 Spring 社区给出的一些建议,但没有创建模拟并且自动连接失败:
<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.package.Dao" />
</bean>
The error I currently encounter is as follows:
我目前遇到的错误如下:
...
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)
If I set the constructor-argvalue to something invalid no error occurs when starting the application context.
如果我将该constructor-arg值设置为无效的值,则在启动应用程序上下文时不会发生错误。
回答by amra
The best way is:
最好的办法是:
<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.package.Dao" />
</bean>
Update
In the context file this mock must be listed before any autowired field depending on it is declared.
更新
在上下文文件中,这个模拟必须在任何依赖于它的自动装配字段之前列出。
回答by Greg Beauchamp
@InjectMocks
private MyTestObject testObject;
@Mock
private MyDependentObject mockedObject;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
This will inject any mocked objects into the test class. In this case it will inject mockedObject into the testObject. This was mentioned above but here is the code.
这会将任何模拟对象注入测试类。在这种情况下,它会将 mockedObject 注入 testObject。这是上面提到的,但这里是代码。
回答by Piotr Gwiazda
I have a very simple solution using Spring Java Config and Mockito:
我有一个使用 Spring Java Config 和 Mockito 的非常简单的解决方案:
@Configuration
public class TestConfig {
@Mock BeanA beanA;
@Mock BeanB beanB;
public TestConfig() {
MockitoAnnotations.initMocks(this); //This is a key
}
//You basically generate getters and add @Bean annotation everywhere
@Bean
public BeanA getBeanA() {
return beanA;
}
@Bean
public BeanB getBeanB() {
return beanB;
}
}
回答by Paul Croarkin
Given:
鉴于:
@Service
public class MyService {
@Autowired
private MyDAO myDAO;
// etc
}
You can have the class that is being tested loaded via autowiring, mock the dependency with Mockito, and then use Spring's ReflectionTestUtils to inject the mock into the class being tested.
您可以通过自动装配加载正在测试的类,使用 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
}
Please note that before Spring 4.3.1, this method won't work with services behind a proxy (annotated with @Transactional, or Cacheable, for example). This has been fixed by SPR-14050.
请注意,在 Spring 4.3.1 之前,此方法不适用于代理后面的服务(例如用@Transactional、 或注释Cacheable)。这已由SPR-14050修复。
For earlier versions, a solution is to unwrap the proxy, as described there: Transactional annotation avoids services being mocked(which is what ReflectionTestUtils.setFielddoes by default now)
对于早期版本,解决方案是解开代理,如此处所述:事务注释避免服务被模拟(ReflectionTestUtils.setField现在默认情况下这样做)
回答by jfcorugedo
If you're using Spring Boot 1.4, it has an awesome way of doing this. Just use new brand @SpringBootTeston your class and @MockBeanon the field and Spring Boot will create a mock of this type and it will inject it into the context (instead of injecting the original one):
如果你使用的是 Spring Boot 1.4,它有一个很棒的方法来做到这一点。只需@SpringBootTest在您的班级和@MockBean场地上使用新品牌,Spring Boot 就会创建一个这种类型的模拟,并将其注入上下文(而不是注入原始的):
@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");
}
}
On the other hand, if you're not using Spring Boot or are you using a previous version, you'll have to do a bit more work:
另一方面,如果你没有使用 Spring Boot 或者你使用的是以前的版本,你将不得不做更多的工作:
Create a @Configurationbean that injects your mocks into Spring context:
创建一个@Configuration将模拟注入 Spring 上下文的bean:
@Configuration
@Profile("useMocks")
public class MockConfigurer {
@Bean
@Primary
public MyBean myBeanSpy() {
return mock(MyBean.class);
}
}
Using @Primaryannotation you're telling spring that this bean has priority if no qualifier are specified.
使用@Primary注释告诉 spring,如果没有指定限定符,则此 bean 具有优先级。
Make sure you annotate the class with @Profile("useMocks")in order to control which classes will use the mock and which ones will use the real bean.
确保您对类进行注释,@Profile("useMocks")以控制哪些类将使用模拟,哪些将使用真正的 bean。
Finally, in your test, activate userMocksprofile:
最后,在您的测试中,激活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() {
....
}
}
If you don't want to use the mock but the real bean, just don't activate useMocksprofile:
如果您不想使用模拟而是使用真正的 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() {
....
}
}
回答by Doug Moscrop
Since 1.8.3Mockito has @InjectMocks- this is incredibly useful. My JUnit tests are @RunWiththe MockitoJUnitRunnerand I build @Mockobjects that satisfy all the dependencies for the class being tested, which are all injected when the private member is annotated with @InjectMocks.
从1.8.3Mockito 开始@InjectMocks- 这非常有用。我的JUnit测试是@RunWith在MockitoJUnitRunner和我建立@Mock满足所有被测的类的依赖关系,这是当私有成员与所有注解注入对象@InjectMocks。
I @RunWiththe SpringJUnit4Runnerfor integration tests only now.
我@RunWith的SpringJUnit4Runner集成测试只是现在。
I will note that it does not seem to be able to inject List<T>in the same manner as Spring. It looks only for a Mock object that satisfies the List, and will not inject a list of Mock objects. The workaround for me was to use a @Spyagainst a manually instantiated list, and manually .add the mock object(s) to that list for unit testing. Maybe that was intentional, because it certainly forced me to pay close attention to what was being mocked together.
我会注意到它似乎无法List<T>以与 Spring 相同的方式注入。它只查找满足 的 Mock 对象,List不会注入 Mock 对象列表。我的解决方法是@Spy对手动实例化的列表使用 a ,并手动将模拟对象添加到该列表以进行单元测试。也许这是故意的,因为它确实迫使我密切关注被一起嘲笑的内容。
回答by teabot
Update:There are now better, cleaner solutions to this problem. Please consider the other answers first.
更新:现在有更好、更干净的解决方案来解决这个问题。请先考虑其他答案。
I eventually found an answer to this by ronen on his blog. The problem I was having is due to the method Mockito.mock(Class c)declaring a return type of Object. Consequently Spring is unable to infer the bean type from the factory method return type.
我最终在 ronen 的博客上找到了这个问题的答案。我遇到的问题是由于方法Mockito.mock(Class c)声明了Object. 因此 Spring 无法从工厂方法返回类型推断 bean 类型。
Ronen's solutionis to create a FactoryBeanimplementation that returns mocks. The FactoryBeaninterface allows Spring to query the type of objects created by the factory bean.
Ronen 的解决方案是创建一个FactoryBean返回模拟的实现。该FactoryBean接口允许 Spring 查询工厂 bean 创建的对象类型。
My mocked bean definition now looks like:
我的模拟 bean 定义现在看起来像:
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
<property name="type" value="com.package.Dao" />
</bean>
回答by Ryan Walls
As of Spring 3.2, this is no longer an issue. Spring now supports Autowiring of the results of generic factory methods. See the section entitled "Generic Factory Methods" in this blog post: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/.
从 Spring 3.2 开始,这不再是问题。Spring 现在支持自动装配泛型工厂方法的结果。请参阅此博客文章中标题为“通用工厂方法”的部分:http: //spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/。
The key point is:
关键是:
In Spring 3.2, generic return types for factory methods are now properly inferred, and autowiring by type for mocks should work as expected. As a result, custom work-arounds such as a MockitoFactoryBean, EasyMockFactoryBean, or Springockito are likely no longer necessary.
在 Spring 3.2 中,现在可以正确推断工厂方法的泛型返回类型,并且模拟的按类型自动装配应该按预期工作。因此,可能不再需要诸如 MockitoFactoryBean、EasyMockFactoryBean 或 Springockito 之类的自定义变通方法。
Which means this should work out of the box:
这意味着这应该是开箱即用的:
<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.package.Dao" />
</bean>
回答by Markus T
If you're using spring >= 3.0, try using Springs @Configurationannotation to define part of the application context
如果您使用的是spring >= 3.0,请尝试使用 Springs@Configuration注释来定义应用程序上下文的一部分
@Configuration
@ImportResource("com/blah/blurk/rest-of-config.xml")
public class DaoTestConfiguration {
@Bean
public ApplicationService applicationService() {
return mock(ApplicationService.class);
}
}
If you don't want to use the @ImportResource, it can be done the other way around too:
如果您不想使用@ImportResource,也可以反过来:
<beans>
<!-- rest of your config -->
<!-- the container recognize this as a Configuration and adds it's beans
to the container -->
<bean class="com.package.DaoTestConfiguration"/>
</beans>
For more information, have a look at spring-framework-reference : Java-based container configuration
有关更多信息,请查看 spring-framework-reference :基于 Java 的容器配置
回答by Kamil
Below code works with autowiring - it is not the shortest version but useful when it should work only with standard spring/mockito jars.
下面的代码适用于自动装配——它不是最短的版本,但在它只适用于标准 spring/mockito jar 时很有用。
<bean id="dao" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"> <bean class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.package.Dao" /> </bean> </property>
<property name="proxyInterfaces"> <value>com.package.Dao</value> </property>
</bean>

