mockito使用教程
mockito主要是为了解决在junit测试过程中,对部分方法进行mock,类似于对方法的实现做了一些“挡板”。当对方法进行调用时,可以实现对待测试方法的各种注入,模拟等
1. 如何添加到项目
- maven引入
在pom中加入以下内容,版本号随最新mockito版本
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.2.4</version>
</dependency>
2. 用法部分总结
2.1 通过mock()方法构造出对象,并使用verify()方法校验对象的方法是否已经被调用
例:
public void testListMockito() {
List mockedList = mock(List.class);
// 对lict做 add() clear() 操作
mockedList.add("one");
mockedList.clear();
// 对对lict的 add() clear() 操作进行校验,应通过
verify(mockedList).add("one");
verify(mockedList).clear();
// 对list的 get方法做校验,应无法通过
verify(mockedList).get(0);
}
2.2 对mock()构造的对象,方法调用的结果进行替换,当该方法被调用时,就会返回固定结果
例:
@Test
public void testStubMockito() {
LinkedList mockedList = mock(LinkedList.class);
// 将 mockedList.get(0)方法的调用结果替换为 "first"
when(mockedList.get(0)).thenReturn("first");
// 返回 "first"
System.out.println(mockedList.get(0));
// 返回 null
System.out.println(mockedList.get(999));
// 返回 "first"
System.out.println(mockedList.get(0));
}
2.3 mockito可以构建出任意参数传入待测试的方法中进行Stub,类似参数匹配,当下一次调用时,就会返回固定的结果
LinkedList mockedList = mock(LinkedList.class);
//当调用 mockedList get()方法时,固定返回element
when(mockedList.get(anyInt())).thenReturn("element");
//自定义参数值,只有当传入的参数满足条件时,才会在调用contains()方法时返回固定 true
when(mockedList.contains(argThat(isValid()))).thenReturn(true);
//following prints "element"
System.out.println(mockedList.get(999));
//you can also verify using an argument matcher
verify(mockedList).get(anyInt());
//argument matchers can also be written as Java 8 Lambdas
verify(mockedList).add(argThat(someString -> someString.length() > 5));
2.3 校验方法调用次数
可以判断某个方法在传入某一类参数调用时,调用的次数。
- times(1) 被调用了一次
- never() 从未被调用
- atMost(1) 最多被调用一次
- atLeastOnce() 最少被调用一次
- atLeast(2) 最少被调用2次
//using mock
mockedList.add("once");
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
2.4 当调用方法时,绑定跑出异常
doThrow(new RuntimeException()).when(mockedList).clear();
//将抛出异常 RuntimeException:
mockedList.clear();
2.5 校验方法调用顺序
- 校验一个对象的方法被调用的顺序,使用inOrder()方法
List singleMock = mock(List.class);
//两次调用add()方法
singleMock.add("was added first");
singleMock.add("was added second");
InOrder inOrder = inOrder(singleMock);
//following will make sure that add is first called with "was added first", then with "was added second"
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");
- 对于多个mock对象的方法执行顺序,也可以使用inOrder()
List firstMock = mock(List.class);
List secondMock = mock(List.class);
firstMock.add("was called first");
secondMock.add("was called second");
firstMock.add("was called third");
InOrder inOrder = inOrder(firstMock, secondMock);
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
inOrder.verify(firstMock).add("was called third");
2.6 使用verifyZeroInteractions()方法保证mock对象未被调用过
例如:
List mockOne = mock(List.class);
List mockTwo = mock(List.class);
List mockThree = mock(List.class);
mockOne.add("one");
verify(mockOne).add("one");
verify(mockOne, never()).add("two");
// mockTwo mockThree 未被使用过
verifyZeroInteractions(mockTwo, mockThree);
当需要判断一个mock对象是否被用过,可以是否verifyNoMoreInteractions()
2.7 使用@Mock注解创建mock对象
- 减少重复mock对象创建代码
- 增加可读性
public class ArticleManagerTest {
@Mock
private LinkedList<String> mockList;
@BeforeClass
public void init() {
// 这块代码必须加上,否则无法初始化,也可以使用MockitoJUnitRunner 或 MockitoRule
MockitoAnnotations.initMocks(this);
}
@Test
public void testListMockito() {
mockList.add("one");
verify(mockList).add("one");
}
}
2.8 连续方法调用(类似于迭代方式)
先构建完成mock对象,当有连续几次对该对象的方法进行调用时,会根据每次的调用次序依次返回相应的结果。而后面所有的返回值都跟最后一次的一样,例如:
when(mockList.get(0))
.thenThrow(new RuntimeException())
.thenReturn("foo");
// 抛出异常
mockList.get(0);
// 返回 "foo"
System.out.println(mockList.get(0));
// 依然返回 "foo"
System.out.println(mockList.get(0));
注意:如果有多个 when-thenReturn 则后一个会将前一个覆盖
2.8 自定义一些注入返回方法
使用thenAnswer()方法,同时再根据Answer接口,实现自定义内容。
when(mockList.get(0)).thenAnswer(
new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return "called with arguments: " + Arrays.toString(args);
}
});
//Following prints "called with arguments: [foo]"
System.out.println(mockList.get(0));
2.9 当待mock替换的方法的参数为空时,需要使用其他方式进行stub
主要使用doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod()等方法。
使用方法如下:
doThrow(new RuntimeException()).when(mockedList).clear();
// 抛出异常
mockedList.clear();
2.10 使用spy()创建真实对象进行mock,区别在于会真实执行相应对象的方法
List list = new LinkedList();
List spy = spy(list);
//可以对方法进行stub
when(spy.size()).thenReturn(100);
//调用真实方法
spy.add("one");
spy.add("two");
//prints "one"
System.out.println(spy.get(0));
//100
System.out.println(spy.size());
//verify
verify(spy).add("one");
verify(spy).add("two");
在使用spy()方法时,需要注意doReturn|Answer|Throw()等方法进行stub
List list = new LinkedList();
List spy = spy(list);
//因为调用的是真实方法,会导致空指针。
when(spy.get(0)).thenReturn("foo");
//正确方式
doReturn("foo").when(spy).get(0);
2.11 修改unstubbed调用的默认值
相当于将一个类中的
Foo mock = mock(Foo.class, Mockito.RETURNS_SMART_NULLS);
Foo mockTwo = mock(Foo.class, new YourOwnAnswer());
- TODO 还有部分功能比较深入,有空继续学习
- 翻译自mockito的官网手册(https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html)