1. 概述

在这个简短教程中,我们将专注于使用Mockito模拟void方法。

与之前关于Mockito框架的文章(如Mockito验证Mockito的When/ThenMockito的Mock方法)一样,下面的MyList类将在测试用例中作为协作方。

我们将为这个教程添加一个新的方法:

public class MyList extends AbstractList<String> {
 
    @Override
    public void add(int index, String element) {
        // no-op
    }
}

2. 简单的模拟与验证

void方法可以与Mockito的doNothing()doThrow()doAnswer()方法一起使用,使模拟和验证变得直观:

@Test
public void whenAddCalled_thenVerified() {
    MyList myList = mock(MyList.class);
    doNothing().when(myList).add(isA(Integer.class), isA(String.class));
    myList.add(0, "");
 
    verify(myList, times(1)).add(0, "");
}

然而,doNothing()是Mockito对void方法的默认行为。

这个版本的whenAddCalledVerified()与上面的实现相同:

@Test
void whenAddCalled_thenVerified() {
    MyList myList = mock(MyList.class);
    myList.add(0, "");
 
    verify(myList, times(1)).add(0, "");
}

doThrow()会抛出一个异常:

@Test
void givenNull_whenAddCalled_thenThrowsException() {
    MyList myList = mock(MyList.class);
    
    assertThrows(Exception.class, () -> {
    doThrow().when(myList).add(isA(Integer.class), isNull());
    });

    myList.add(0, null);
}

我们将在下面讨论doAnswer()

3. 参数捕获

使用doNothing()覆盖默认行为的一个原因是捕获参数。

在上述例子中,我们使用verify()方法检查传递给add()的方法参数。

然而,我们可能需要捕获参数并对其做更多的处理。

在这种情况下,我们像上面那样使用doNothing(),但使用一个ArgumentCaptor

@Test
void givenArgumentCaptor_whenAddCalled_thenValueCaptured() {
    MyList myList = mock(MyList.class);
    
    ArgumentCaptor<String> valueCapture = ArgumentCaptor.forClass(String.class);
    doNothing().when(myList).add(any(Integer.class), valueCapture.capture());
    
    myList.add(0, "captured");
    
    assertEquals("captured", valueCapture.getValue());
}

4. 回答对void的调用

一个方法可能执行的不仅仅是添加或设置值的简单行为。

对于这些情况,我们可以使用Mockito的Answer来添加我们需要的行为:

@Test
void givenDoAnswer_whenAddCalled_thenAnswered() {
    MyList myList = mock(MyList.class);
    
    doAnswer(invocation -> {
        Object arg0 = invocation.getArgument(0);
        Object arg1 = invocation.getArgument(1);
        
        assertEquals(3, arg0);
        assertEquals("answer me", arg1);
        return null;
    }).when(myList).add(any(Integer.class), any(String.class));
    
    myList.add(3, "answer me");
}

Mockito的Java 8特性所述,我们使用带有Answer的lambda来为add()定义自定义行为。

5. 部分模拟

部分模拟也是一个选择。Mockito的doCallRealMethod()也可以用于void方法:

@Test
void givenDoCallRealMethod_whenAddCalled_thenRealMethodCalled() {
    MyList myList = mock(MyList.class);
    
    doCallRealMethod().when(myList).add(any(Integer.class), any(String.class));
    myList.add(1, "real");
    
    verify(myList, times(1)).add(1, "real");
}

这样,我们可以在同时调用实际方法并进行验证。

6. 总结

在这篇简短的文章中,我们介绍了四种处理Mockito测试中的void方法的不同方法。

如往常一样,示例代码可以在这个GitHub项目中找到。