官方资料:https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html

1、验证某些行为

以List集合为例,实际测试用原本的实例代替

//导包
import static org.mockito.Mockito.*;

//创造mock对象
List mockedList = mock(List.class);

//使用mock对象
mockedList.add("one");
mockedList.clear();

//验证
verify(mockedList).add("one");
verify(mockedList).clear();

一旦创建,mock将记住所有交互。然后你可以有选择地验证你感兴趣的交互。

2、stubbing(存根)

//mock LinkedList对象
LinkedList mockedList = mock(LinkedList.class);

//stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());

//打印"first"
System.out.println(mockedList.get(0));

//打印异常信息
System.out.println(mockedList.get(1));

//打印null,因为get(999)没有存根
System.out.println(mockedList.get(999));

//如果您的代码不关心get(0)返回什么,那么它不应该被存根。
verify(mockedList).get(0);

注意:

  • 一旦存根,该方法将始终返回一个存根值,无论调用了多少次。

3、参数匹配器

//使用内置的anyInt()参数匹配器存根
when(mockedList.get(anyInt())).thenReturn("element");

//使用自定义参数匹配器
when(mockedList.contains(argThat(isValid()))).thenReturn(true);

//打印"element"
System.out.println(mockedList.get(999));

//使用参数匹配器来验证
verify(mockedList).get(anyInt());

//Java 8 可以使用Lambda表达式来验证
verify(mockedList).add(argThat(someString -> someString.length() > 5));

自定义参数匹配器

class ListOfTwoElements implements ArgumentMatcher<List> {
     public boolean matches(List list) {
         return list.size() == 2;
     }
     public String toString() {
         //printed in verification errors
         return "[list of 2 elements]";
     }
 }

 List mock = mock(List.class);

 when(mock.addAll(argThat(new ListOfTwoElements()))).thenReturn(true);

 mock.addAll(Arrays.asList("one", "two"));

 verify(mock).addAll(argThat(new ListOfTwoElements()));

在Java 8 中可以使用Lambda表达式来代替上述实现:

verify(mock).addAll(argThat(list -> list.size() == 2));

更多的参数匹配器参考:https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/ArgumentMatchers.html

4、验证准确的调用数量/至少x次 /从不

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.LinkedList;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class PersonTest {
    @Test
    public void test(){
        LinkedList mockedList = Mockito.mock(LinkedList.class);
        mockedList.add("once");

        mockedList.add("twice");
        mockedList.add("twice");

        mockedList.add("three times");
        mockedList.add("three times");
        mockedList.add("three times");

        //下面的两个验证工作完全相同,times缺省情况下默认是1
        verify(mockedList).add("once");
        verify(mockedList, times(1)).add("once");

        //准确的调用数验证
        verify(mockedList, times(2)).add("twice");
        verify(mockedList, times(3)).add("three times");

        //验证从未使用
        verify(mockedList, never()).add("never happened");

        //验证最少使用次数和最多使用次数
        verify(mockedList, atMostOnce()).add("once");
        verify(mockedList, atLeastOnce()).add("three times");
        verify(mockedList, atLeast(2)).add("three times");
        verify(mockedList, atMost(5)).add("three times");
    }
}

5、使用异常stub void方法

doThrow(new RuntimeException()).when(mockedList).clear();

//抛出异常:RuntimeException:
mockedList.clear();

6、验证顺序

A. 单个mock必须按特定顺序调用其方法

List singleMock = mock(List.class);

//using a single mock
singleMock.add("was added first");
singleMock.add("was added second");

//为单个mock创建一个inOrder验证器
InOrder inOrder = inOrder(singleMock);

//下面将确保add第一次调用时使用"was added first",然后使用"was added second"
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");

B. 多个mock必须按特定顺序使用

List firstMock = mock(List.class);
List secondMock = mock(List.class);

//使用 mocks
firstMock.add("was called first");
secondMock.add("was called second");

//创建inOrder对象,任何mocks必须按照顺序验证通过
InOrder inOrder = inOrder(firstMock, secondMock);

//follow将确保firstMock在secondMock之前被调用
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");

7、确保交互不会发生

//使用mocks -只有mockOne是交互的
mockOne.add("one");

//普通的验证
verify(mockOne).add("one");

//验证该方法从未在mock中调用
verify(mockOne, never()).add("two");

//验证其他模拟没有交互
verifyZeroInteractions(mockTwo, mockThree);

8、发现多余的交互

mockedList.add("one");
mockedList.add("two");

verify(mockedList).add("one");

//以下验证将失败
verifyNoMoreInteractions(mockedList);

不建议在每个测试方法中使用verifyNoMoreInteractions()。verifyNoMoreInteractions()是来自交互测试工具包的一个方便的断言。只在相关的场合使用。滥用它会导致过度指定、更不容易维护的测试。

9、@Mock注释

@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class ExampleTest {

    @Mock
    private List list;

    @Test
    public void shouldDoSomething() {
        list.add(100);
    }
}

10、存根连续调用

对同一个方法调用使用不同的返回值/异常存根。

when(mock.someMethod("some arg"))
  .thenThrow(new RuntimeException())
  .thenReturn("foo");

//第一次调用:抛出运行时异常:
mock.someMethod("some arg");

//第二次调用:打印"foo"
System.out.println(mock.someMethod("some arg"));

//后续任何调用都打印"foo"
System.out.println(mock.someMethod("some arg"));

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

阿里巴巴Java开发手册 Previous
程序员必备小工具 Next