Mockito注解的基础知识
本教程重点介绍了Mockito从基本到更高级的用例,以及与其他的测试工具(如Junit)的集成。
原文:https://www.baeldung.com/mockito-annotations
翻译:杨金龙
Mockito 基础
- 使用Mockito
@Mock
,@Spy
,@Captor
,@InjectMocks
1. 概述
本章节介绍Mockito的注解,@Mock, @Spy, @Captor, and @InjectMocks。
2. 启用Mockito注解
我们先来探索一下使用注解启用Mockito的几种方式:
2.1 MockitoJUnitRunner
第一种方式是在JUnit测试中使用注解@RunWith(MockitoJUnitRunner.class)
,如下所示:
@RunWith(MockitoJUnitRunner.class)
public class MockitoAnnotationTest {
...
}
2.2 MockitoAnnotations.initMocks()
可以通过调用MockitoAnnotations.initMocks()
启用Mockito, 如下所示:
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
2.3 MockitoJUnit.rule()
还可以使用MockitoJUnit.rule(),如下所示
public class MockitoInitWithMockitoJUnitRuleUnitTest {
@Rule
public MockitoRule initRule = MockitoJUnit.rule();
...
}
这种情况下,rule
必须是public
的。
3. @Mock 注解
在Mockito中,使用最广泛的注解是@Mock,可以使用@Mock注解创建和注入模拟实例,不需要手动调用Mockito.mock。
在下面的示例中,我们将使用手动的方式创建一个模拟的ArrayList(不使用@Mock注解):
@Test
public void whenNotUseMockAnnotationThenCorrect() {
List mockList = Mockito.mock(ArrayList.class);
mockList.add("one");
Mockito.verify(mockList).add("one");
assertEquals(0, mockList.size());
Mockito.when(mockList.size()).thenReturn(100);
assertEquals(100, mockList.size());
}
现在我们再使用@Mock注解注入模拟对象,重新实现上面的示例:
@Mock
List<String> mockedList;
@Test
public void whenUseMockAnnotationThenMockIsInjected() {
mockedList.add("one");
Mockito.verify(mockedList).add("one");
assertEquals(0, mockedList.size());
Mockito.when(mockedList.size()).thenReturn(100);
assertEquals(100, mockedList.size());
}
注意在上面两个示例中,我们是如何与模拟对象进行交互并且验证其中的一些交互的,只是为了确保模拟对象的行为是正确的。
4. @Spy 注解
现在,我们来看看如何使用@Spy注解来监视现有的实例。
在下面的实例中,我们使用旧方式创建一个监视List(不使用@Spy
)
@Test
public void whenNotUseSpyAnnotationThenCorrect() {
List<String> spyList = Mockito.spy(new ArrayList<String>());
spyList.add("one");
spyList.add("two");
Mockito.verify(spyList).add("one");
Mockito.verify(spyList).add("two");
assertEquals(2, spyList.size());
Mockito.doReturn(100).when(spyList).size();
assertEquals(100, spyList.size());
}
我们再使用@Spy
注解,对spyList
执行同样的监视操作
@Spy
List<String> spiedList = new ArrayList<String>();
@Test
public void whenUseSpyAnnotation_thenSpyIsInjectedCorrecttly(){
spiedList.add("one");
spiedList.add("two");
Mockito.verify(spiedList).add("one");
Mockito.verify(spiedList).add("two");
assertEquals(2, spiedList.size());
Mockito.doReturn(100).when(spiedList).size();
assertEquals(100, spiedList.size());
}
注意,就像之前描述的,我们在这里和监视对象进行交互,以确保它正确的工作,在这个例子中,
- 我们使用了真实的spyList.add()方法将元素添加到spyList
- 使用Mockito.doReturn()来将spyList.size()返回值返回100来替代原本的长度2
5. @Captor
注解
接下来,我们学习如何使用@Captor
注解来创建一个ArgumentCaptor
实例。
首先,我们先在不使用@Captor
注解的情况下,创建一个ArgumentCaptor
:
@Test
public void whenNotUseCaptorAnnotation_thenCorrect(){
List mockList = Mockito.mock(List.class);
ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);
mockList.add("one");
Mockito.verify(mockList).add(arg.capture);
assertEquals("one", arg.getValue());
}
然后,我们使用@Captor来创建一个ArgumentCaptor
实例来达到相同的目的:
@Mock
List mockedList;
@Captor
ArgumentCapror argCaptor;
@Test
public void whenUseCaptorAnnotation_thenTheSame(){
mockedList.add("jack");
Mockito.verify(mockedList).add(argCaptor.capture());
assertEquals("jack", argCaptor.getValue());
}
注意当去掉配置逻辑后,测试是如何变得更简单和更易读的。
6. @InjectMocks
注解
现在,我们讨论一下如何使用@InjectMocks注解将模拟字段自动的注入到测试对象中。
In the following example – we use @InjectMocks to inject the mock wordMap into the MyDictionary dic:
在下面的例子中,我们用@InjectMocks
注解将模拟的wordMap
注入到 MyDictionary dic
中
@Mock
Map<String, String> wordMap;
@InjectMocks
MyDictionary dict = new MyDictionary();
@Test
public void whenUseInjectMocksAnnotation_thenCorrect(){
Mockito.when(wordMap.get("aWord")).thenRetuen("aMeaning");
assertEquals("aMeaning", dict.getMeaning("aWord"));
}
下面是MyDictionary
类:
public class MyDictionary {
Map<String, String> wordMap;
public MyDictionary() {
wordMap = new HashMap<String, String>();
}
public void add(final String word, final String meaning) {
wordMap.put(word, meaning);
}
public String getMeaning(final String word) {
return wordMap.get(word);
}
}
7. 将Mock对象注入Spy
类似于上面的测试,我们可能需要给一个Spy对象注入一个Mock对象:
@Mock
Map<String, String> wordMap;
@Spy
MyDictionary spyDict = new MyDictionary();
然而Mockito并不支持向Spy对象注入Mock,下面的测试结果是一个例外:
@Test
public void whenUseInjectMocksAnnotation_thenCorrect(){
Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
assertEquals("aMeaning", spyDict.getMeaning("aWord"));
}
如果我们想在Spy对象中使用Mock对象,可以通过构造函数手动注入Mock对象:
MyDictionary(Map<String, String> wordMap){
this.wordMap = wordMap;
}
现在手动创建Spy对象,不需要用注解:
@Mock
Map<String, String> wordMap;
MyDictionary spyDict;
@Before
public void init(){
MocitioAnnotations.initMocks(this);
spyDict = Mockito.spy(new MyDictionay(wordMap));
}
8. 使用Mockito注解时的空指针异常
我们实际使用带有@Mock
或@Spy
注解的实例时,可能会遇到NullPointerException
public class MockitoAnnotationUninitializedUnitTest{
@Mock
List<String> mockedList;
@Test(expected = NullPointerException.class)
public void whenMockitoAnnotationUninitialized_thenNPEThrown(){
Mockito.when(mockedList.size()).thenReturn(1);
}
}
大多数情况下,仅是因为我们忘记正确启用 Mockito 注解。
所以我们必须记住,每次想要使用 Mockito 注解时,必须采取额外的步骤并初始化它们,初始化的步骤在前面的第2小节。
9. 笔记
最后,是一些关于 Mockito 注解的笔记:
- Mockito 注解简化了创建mock对象,使代码的重复性最小化
- 它们使测试更具可读性
- @InjectMocks对于同时注入@Spy和@Mock实例是必需的
10. 总结
在这个快速教程中,我们展示了 Mockito 中注解的基础知识。
所有这些例子的实现都可以在 GitHub 上找到。 这是一个 Maven 项目,很容易导入和运行。
学习更多关于 Mockito 的教程,可以到这里。
(全文完)