![mockito](https://i-blog.csdnimg.cn/blog_migrate/a5e68db4761e0f2e606427b4c79b39a2.png)
mockito
使用Mockito进行Java类的模拟和存根的任何人,可能都熟悉InjectMocks -annotation。 尽管几年前我实际上认为Mockito是Java单元测试中最出色的模拟框架之一,但我在几年前的一篇文章中似乎有点苛刻,为什么不应该使用@InjectMocks自动关联字段。
每个注释都可能不时地使用聚光灯-甚至那些附带安全说明的注释😉因此,我想为什么不对@InjectMocks
表示赞赏?
这是如何工作的? 如果我们要自己实现这种逻辑,那只是看看Mockito是如何设计螺母'n螺栓来初始化被测类的(即用@InjectMocks
注释的@InjectMocks
),以及所有协作的@InjectMocks
直到第一个测试方法是援引?
考虑下面的JUnit 5测试,该测试可以验证女服务员是否可以正确地“做某事”。
@ExtendWith (MockitoExtension. class )
public class WaitressTest {
@Mock
CoffeeMachine coffeeMachine;
@Spy
Toaster toaster;
@InjectMocks
Waitress waitress;
@Test
void should_do_something() {
// ..
}
}
您将在此处看到5种不同的注释:1. JUnit 5的@ExtendWith
2. Mockito的@Mock
3. Mockito的@Spy
4. Mockito的@InjectMocks
5. JUnit 5的@Test
@ExtendWith
是一种在测试运行时让JUnit将控制传递给Mockito的方法。 没有它,Mockito将被排除在循环之外,并且由于所有带注释的字段都为null
,因此测试失败。
因为@Mock
和@Spy
是@Spy
实际支持的仅有两个注释, @InjectMocks
我想我会同时使用它们。 😉Mockito还支持ArgumentCaptor字段上的@Captor
批注,但是我们不在此使用它。
究竟是什么在我们的测试@Test
-annotated方法并不重要要么,但事先需要的Mockito以确保:
- 所有
@Mock
和@Spy
字段(例如CoffeeMachine
和Toaster
)都被初始化为模拟和间谍 Waitress
被创建为一个真实的对象,并且两个合作者都被正确地“注入”到了其中。
开始嘲笑
假设将完整的测试类实例WaitressTest
传递给Mockito的MockitoAnnotations.initMocks()
(还记得,在过去,您必须在测试的设置中手动调用此方法吗?) ,它再次委托给实现该类的类。 AnnotationnEgine
接口,可以由插件配置,也可以来自Mockito的全局配置。
public interface AnnotationEngine {
/**
* Processes the test instance to configure annotated members.
*
* @param clazz Class where to extract field information, check implementation for details
* @param testInstance Test instance
*/
void process(Class clazz, Object testInstance);
}
我们将逐步构建自己的“简化” AnnotationEngine
。
处理模拟
我们必须先扫描测试类和必要的字段被嘲笑:那些与注释@Mock
, @Spy
和@Captor
。
实际上,Mockito首先处理@Mock
和@Captor
批注,然后处理@Spy
字段。
通用机制大量使用反射:遍历测试类的字段,测试每个字段是否存在正确的注释并进行相应的处理。
嘲笑
让我们先使用@Mock
:
// import java.lang.reflect.Field;
public void process(Class clazz, Object testInstance) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Mock. class )) { // 1