Mockito、Mock、Spy、Captor和InjectMocks入门

2023年 7月 26日 58.7k 0

概述

本文将介绍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 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 spyList = Mockito.spy(new ArrayList());
    
    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 spiedList = new ArrayList();

@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 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 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 wordMap;

    public MyDictionary() {
        wordMap = new HashMap();
    }
    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 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 wordMap) {
    this.wordMap = wordMap;
}

我们现在可以手动创建Spy,而不是使用注解:

@Mock
Map 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 mockedList;

    @Test(expected = NullPointerException.class)
    public void whenMockitoAnnotationsUninitialized_thenNPEThrown() {
        Mockito.when(mockedList.size()).thenReturn(1);
    }
}

大多数情况下,发生这种情况只是因为我们忘记启用Mockito注解。

因此,想要使用Mockito注解时,我们都必须采取额外的步骤进行初始化。

结论

Mockito的注解最大限度地减少了重复的mock创建代码,使测试更具可读性。

@InjectMock对于注入@Spy和@Mock实例都是必要的。

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论