因为大部分人都很熟悉这些接口(例如add(),get(),clear()等方法),下面是一个mock个List的例子。
在实际情况下,请直接使用List真实实例,而不要去mock。
//Let's import Mockito statically so that the code looks clearer
// 引入包
import static org.mockito.Mockito.*;
//mock creation
// mock实例
List mockedList = mock(List.class);
//using mock object
// 像List真实实例一样,使用mock对象
mockedList.add("one");
mockedList.clear();
//verification
// 作些验证
verify(mockedList).add("one");
verify(mockedList).clear();
当一个mock实例被创建后,它会记录所有交互行为。然后,你可以根据需要进行验证。
//You can mock concrete classes, not just interfaces
// 不仅是接口,也可以mock具体类
LinkedList mockedList = mock(LinkedList.class);
//stubbing
// 桩函数
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
//following prints "first"
// 下面代码将会打印"first"
System.out.println(mockedList.get(0));
//following throws runtime exception
//下面语句,将会抛出一个异常
System.out.println(mockedList.get(1));
//following prints "null" because get(999) was not stubbed
// 下面语句,由于没有进行打桩,就会得到null
System.out.println(mockedList.get(999));
//Although it is possible to verify a stubbed invocation, usually it's just redundant
// 尽管可以验证一个桩函数的调用,但这通常都是多余的
//If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed).
// 如果你的代码关心get(0)的返回,然后,一些事情会被打破(这通常是在verify()函数被调用前)
//If your code doesn't care what get(0) returns, then it should not be stubbed. Not convinced? See here.
// 如果你的代码不关心get(0)的返回,便不需要打桩了。不服,那就看这里
verify(mockedList).get(0);
默认情况下,所有有返回值的方法,一个mock对象都会返回一个适当值,比如null,或者内置类型或内置类型的装箱值,或者一个空的集合。例如,0作为返回值为int/Integer的返回,false,作为返回值为boolean/Boolean的返回;
桩函数可以被重写:例如,公共桩函数可以用来安装测试夹具(fixture setup),但测试方法可以重写它。请记住重新装函数,通常是表示有潜在代码被打桩太多的味道;
一旦被打桩(stubbed),则方法会总是返回桩值,无论方法被调用多少次。上面的原则很重要——当你对一些方法多次用同一个值进行打桩时。换句话说:打桩的顺序是有关系的(the order of stubbing matters),一般情况下意义不大,只有在桩(stubbing)恰好是同样方法的调用或者使用参数参数匹配器(argument matchers)时。
Mockito通常用Java语法方式的equals()函数来验证参数值。有些时候,当你需要额外的灵活性时,你也可以用参数匹配器
//stubbing using built-in anyInt() argument matcher
// 使用内置的anyInt()参数匹配器来打桩
when(mockedList.get(anyInt())).thenReturn("element");
//stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
// 使用自定义参数匹配器(让我们把这个返回自定义匹配的实现叫做isValid())
when(mockedList.contains(argThat(isValid()))).thenReturn("element");
//following prints "element"
// 下面会打印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
// 参数匹配器也可以使用Java 8 的Lambdas表达式来定义
verify(mockedList).add(someString -> someString.length() > 5);
参数匹配器增加了验证和打桩的灵活性,点击这里或这里可以查看更多内置匹配器和自定义参数匹配器
关于自定义匹配器的信息,可以参考ArgumentMatcher类的javadoc
要合理使用复杂的参数匹配器。通常情况下,匹配都是使用equals()或偶然使用anyX()来实现一个清晰和简洁的测试。有时候重构代码从而使用equals()匹配或者重写equals()方法,都是比使用复杂参数匹配器更好的方式。
更多信息可以阅读15节或者ArgumentCaptor类的javadoc。ArgumentCaptor是一个特殊的参数匹配器实现,可以为后续断言捕获参数值
注意事项:
如果你使用参数匹配器,则所有的参数,都必须是通过匹配器提供
下面是使用匹配器进行验证的例子,打桩也是同样:
verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
//above is correct - eq() is also an argument matcher
// 上面代码是正确的,eq也是一个匹配器
verify(mock).someMethod(anyInt(), anyString(), "third argument");
//above is incorrect - exception will be thrown because third argument is given without an argument matcher.
// 上面的代码是有问题的,因为第三个参数不是匹配器,所以,会抛出异常
像anyObject(),eq()这样的匹配器方法并不会返回匹配器。在这些方法内部,会在栈上记录一个匹配,然后返回一个傀儡值(通常是null)。这些实现是因为Java编译器的静态类型安全。这种实现的后果就是,你不能在verified/stubbed方法以外的地方使用anyObject(), eq()这些方法。
//using mock
mockedList.add("once");
mockedList.add("twice");
mockedList.add("twice");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
//following two verifications work exactly the same - times(1) is used by default
// 下面的两个验证,验证次数——默认是times(1)表示一次
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
//exact number of invocations verification
// 验证指定次数
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");
//verification using never(). never() is an alias to times(0)
// 验证重没有调用,never()是times(0)的一个别名
verify(mockedList, never()).add("never happened");
//verification using atLeast()/atMost()
// 至少/最多调用次数的验证
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("five times");
verify(mockedList, atMost(5)).add("three times");
time(1)是默认值,因此,time(1)是可以省略的。
doThrow(new RuntimeException()).when(mockedList).clear();
//following throws
// 下面的调用会抛出异常
RuntimeException:
mockedList.clear();
有关doThrow或doAnswer函数族的更多信息,可以参考12节
// A. Single mock whose methods must be invoked in a particular order
// 方式A:一个方法被按照指定次序调用的简单mock
List singleMock = mock(List.class);
//using a single mock
// 使用上面的mock
singleMock.add("was added first");
singleMock.add("was added second");
//create an inOrder verifier for a single mock
// 为single mock创建一个InOrder
InOrder inOrder = inOrder(singleMock);
//following will make sure that add is first called with "was added first, then with "was added second"
// 下面的代码会验证add函数第一次调用参数为"was added first",然后参数是"was added second"
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");
// B. Multiple mocks that must be used in a particular order
// 方式B:多个mock对象的按次序使用
List firstMock = mock(List.class);
List secondMock = mock(List.class);
//using mocks
firstMock.add("was called first");
secondMock.add("was called second");
//create inOrder object passing any mocks that need to be verified in order
// 使用多个mock对象的调用次序创建一个InOrder
InOrder inOrder = inOrder(firstMock, secondMock);
//following will make sure that firstMock was called before secondMock
// 下面的代码验证firstMock对象的add调用是先于secondMock对象
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
// Oh, and A + B can be mixed together at will
// 当然,A和B方式是可以混合在一起使用的
验证次序是灵活的,你不用把每个方法的调用都验证一遍,你只需要验证那些你真正需要的。还有,你也可以仅仅使用那些需要验证次序的mock对象来创建InOrder对象。
//using mocks - only mockOne is interacted
//
mockOne.add("one");
//ordinary verification
// 普通验证
verify(mockOne).add("one");
//verify that method was never called on a mock
// 验证一个mock的方法永远没有调用
verify(mockOne, never()).add("two");
// verify that other mocks were not interacted
// 确保其他mock对象没有交互行为发生
verifyZeroInteractions(mockTwo, mockThree);
//using mocks
mockedList.add("one");
mockedList.add("two");
verify(mockedList).add("one");
//following verification will fail
// 下面的验证会失败
verifyNoMoreInteractions(mockedList);
警告:
经常使用古典方式,预期-运行-验证,的人倾向于频繁使用verifyNoMoreInteractions(),甚至在每个测试方法里都使用。但verifyNoMoreInteractions()并不被推荐在每个方法里都使用。verifyNoMoreInteractions()是互动测试工具包里一个便利的断言。仅仅在你需要验证冗余调用的时候使用。滥用verifyNoMoreInteractions()会使测试代码很难维护。
never()是一种更加显示和意图明确的方式。
以上内容翻译自Mockito官方教程的1-8节