概述
本文将介绍Mockito库的以下注释:@Mock、@Spy、@Captor和@InjectMocks。
启用Mockito注释
- MockitoJUnitRunner
第一个选项是用MockitoJUnitRunner注释JUnit测试:
@ExtendWith(MockitoExtension.class)
public class MockitoAnnotationUnitTest {
...
}
- MockitoAnnotations.openMocks()
或者,我们可以通过调用
MockitoAnnotations.openMocks()以编程方式启用Mockito注释:
@BeforeEach
public void init() {
MockitoAnnotations.openMocks(this);
}
@Mock
Mockito中使用最广泛的注释是@Mock。我们可以使用@Mock创建和注入模拟实例,而无需手动调用Mockito.Mock。
在以下示例中,我们将在不使用@Mock注释的情况下手动创建一个模拟的ArrayList:
@Test
public void whenNotUseMockAnnotation_thenCorrect() {
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:
@Mock
List<String> mockedList;
@Test
public void whenUseMockAnnotation_thenMockIsInjected() {
mockedList.add("one");
Mockito.verify(mockedList).add("one");
assertEquals(0, mockedList.size());
Mockito.when(mockedList.size()).thenReturn(100);
assertEquals(100, mockedList.size());
}
@Spy
现在让我们看看如何使用@Spy注解来监视现有实例。
在以下示例中,在不使用@spy注释的情况下创建受监视列表:
@Test
public void whenNotUseSpyAnnotation_thenCorrect() {
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注释:
@Spy
List<String> spiedList = new ArrayList<String>();
@Test
public void whenUseSpyAnnotation_thenSpyIsInjectedCorrectly() {
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());
}
@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
ArgumentCaptor argCaptor;
@Test
public void whenUseCaptorAnnotation_thenTheSame() {
mockedList.add("one");
Mockito.verify(mockedList).add(argCaptor.capture());
assertEquals("one", argCaptor.getValue());
}
@InjectMocks
现在,让我们讨论如何使用@InjectMocks注解将mock字段自动注入到测试对象中。
在以下示例中,我们将使用@InjectMocks将mock wordMap注入MyDictionary dic:
@Mock
Map<String, String> wordMap;
@InjectMocks
MyDictionary dic = new MyDictionary();
@Test
public void whenUseInjectMocksAnnotation_thenCorrect() {
Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
assertEquals("aMeaning", dic.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);
}
}
向Spy注入Mock
我们可能想向Spy注入一个mock:
@Mock
Map<String, String> wordMap;
@Spy
MyDictionary spyDic = new MyDictionary();
然而,Mockito不支持向Spy中注入mock,以下测试导致异常:
@Test
public void whenUseInjectMocksAnnotation_thenCorrect() {
Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
assertEquals("aMeaning", spyDic.getMeaning("aWord"));
}
如果我们想将mock与spy一起使用,我们可以通过构造函数手动注入mock:
MyDictionary(Map<String, String> wordMap) {
this.wordMap = wordMap;
}
我们现在可以手动创建Spy,而不是使用注解:
@Mock
Map<String, String> wordMap;
MyDictionary spyDic;
@BeforeEach
public void init() {
MockitoAnnotations.openMocks(this);
spyDic = Mockito.spy(new MyDictionary(wordMap));
}
现在测试将通过。
使用注解时遇到NPE
通常,当我们试图实际使用@Mock或@Spy注解的实例时,我们可能会遇到NullPointerException:
public class MockitoAnnotationsUninitializedUnitTest {
@Mock
List<String> mockedList;
@Test(expected = NullPointerException.class)
public void whenMockitoAnnotationsUninitialized_thenNPEThrown() {
Mockito.when(mockedList.size()).thenReturn(1);
}
}
大多数情况下,发生这种情况只是因为我们忘记启用Mockito注解。
因此,想要使用Mockito注解时,我们都必须采取额外的步骤进行初始化。
结论
Mockito的注解最大限度地减少了重复的mock创建代码,使测试更具可读性。
@InjectMock对于注入@Spy和@Mock实例都是必要的。