前言
上一篇的《Mockito-入门》主要是让大家对Mockito有个初步的认识,本篇则是对官方文档对Mockito的介绍进行解释。大家也可以去http://mockito.googlecode.com/svn/branches/1.6/javadoc/org/mockito/Mockito.html进行查看。
正题
Mockito的主要用途就是用来模拟对象(Mock)、验证方法被调用次数(Verification)、返回期望值(Stubbing)。
1.Let's verify some behaviour!-验证行为
Once created, mock will remember all interactions.——来自官网
即一旦一个mock对象被创建,那么该对象所有交互行为都会被记住。比如下面例子中它可以记住mockedList某个方法是否被调用过或者被调用过几次。
import static org.mockito.Mockito.*;
import java.util.List;
import org.junit.Test;
public class TestMockito {
@Test
public void testMockito1(){
// 创建模拟对象
List mockedList = mock(List.class);
//using mock object
mockedList.add("one");
mockedList.clear();
// 验证add方法是否在前面被调用了一次,且参数为“one”。clear方法同样。
verify(mockedList).add("one");
verify(mockedList).clear();
// 下面的验证会失败。因为没有调用过add("two")。
verify(mockedList).add("two");
}
}
2.How about some stubbing? -返回期望值
By default, for all methods that return value, mock returns null, an empty collection or appropriate primitive/primitive wrapper value (e.g: 0, false, ... for int/Integer, boolean/Boolean, ...).——来自官网
对于有返回值但没有设置期望值的模拟对象,Mockito会返回相应的默认值,内置类型int会返回0,boolean返回false,其他则返回null。
这个返回默认值主要是因为Mock对象会覆盖(override)整个被Mock的对象的方法,所以没有设置期望值的就只能返回默认值了。
@Test
public void testMockito2() {
// 我们不仅可以模拟接口还可以模拟具体类。
LinkedList mockedList = mock(LinkedList.class);
// stubbing 当get(0)被调用时,返回"first". 方法get(1)被调用时,抛异常
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
// 下面会输出"first"
System.out.println(mockedList.get(0));
// 下面会输出"null",因为999没有被设置期望值
System.out.println(mockedList.get(999));
// 下面会抛出异常,因为设置的返回值是异常(想要testMokito2测试通过,将下面这一行get(1)注释即可)
System.out.println(mockedList.get(1));
//重复stub两次,则以第二次为准。如下将返回"second":
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(0)).thenReturn("second");
// 输出"second"
System.out.println(mockedList.get(0));
//如果是下面这种形式,则表示第一次调用时返回“first”,第二次调用时返回“second”。可以写n多个。
when(mockedList.get(0)).thenReturn("first").thenReturn("second");
// 输出"first"
System.out.println(mockedList.get(0));
// 输出"second"
System.out.println(mockedList.get(0));
// 输出"second"
System.out.println(mockedList.get(0));
}
3.Argument matchers-参数匹配
If you are using argument matchers, all arguments have to be provided by matchers.——来自官网
有关参数匹配很重要的一点:一旦某个方法的参数使用了参数匹配,则该方法所有的参数都得使用参数匹配。
@Test
public void testMockito3() {
LinkedList mockedList = mock(LinkedList.class);
// 使用內置的參數匹配函數anyInt()
when(mockedList.get(anyInt())).thenReturn("element");
// 也可以使用自定義的匹配類,比如下面的isValid就是一個自定義的匹配器
when(mockedList.contains(argThat(isValid()))).thenReturn(false);
// 下面會輸出"element"
System.out.println(mockedList.get(999));
// 也可以通過參數匹配方法進行驗證
verify(mockedList).get(anyInt());
// 下面的方法會報錯,因為第一個參數沒有使用參數匹配,而第二個參數使用了參數匹配
verify(mockedList).set(1, anyString());
}
private Matcher<String> isValid(){
return Matchers.any();
}
4.Verifying exact number of invocations / at least x / never-验证调用的具体次数/最少次数/从未调用
times(1) is the default. Therefore using times(1) explicitly can be omitted.——来自官网
默认调用一次,所以对于1次我们可以省略掉times(1).
@Test
public void testMockito4() {
LinkedList mockedList = mock(LinkedList.class);
// using mock
mockedList.add("once");
mockedList.add("twice");
mockedList.add("twice");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
// 下面兩種寫法都是針對調用1次,因為1是默認的,我們一般使用第一種寫法就可以
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
// 指定了具體調用的次數
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");
// 使用了never(),即times(0)
verify(mockedList, never()).add("never happened");
// 使用了最多多少次,最少多少次
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("three times");
verify(mockedList, atMost(5)).add("three times");
}
5.Stubbing void methods with exceptions-设置void方法的返回值为抛异常
Mockito起初有一个stubVoid(Object)方法专门针对void方法的调用,但是stubVoid()方法被弃用换成了doThrow(Throwable),为了与doAnswer(Answer)保持一致,对于doThrow与doAnswer会在后面12详细讲解。
6.Verification in order-验证调用顺序
Verification in order is flexible - you don't have to verify all interactions one-by-one but only those that you are interested in testing in order.——来自官网
Mockito对于顺序的验证是比较灵活的,你不必一一验证所有的调用,只需要验证你所需要的即可。
@Test
public void testMockito6(){
List firstMock = mock(List.class);
List secondMock = mock(List.class);
//using mocks
firstMock.add("was called first");
secondMock.add("was called second");
// 创建InOrder对象时只需要传入你所需要验证顺序的Mock对象即可
InOrder inOrder = inOrder(firstMock, secondMock);
// 下面这两个是正确的,调用顺序正确
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
// 下面这两个会失败,因为调用的顺序出错了
inOrder.verify(secondMock).add("was called second");
inOrder.verify(firstMock).add("was called first");
}
小结:
由于篇幅过长,我将方法介绍分为了两部分,一共有十三点,后七点会在下一篇继续介绍。