简介
使用Mock总来说只有四步操作:
- mock creation
- stub method calls
- Use mock object
- verify interactions
声明mockito依赖
org.mockito mockito-all LATEST
1.验证行为
一旦创建 mock 将会记得所有的交互。你可以选择验证你感兴趣的任何交互
//Let's import Mockito statically so that the code looks clearerimport static org.mockito.Mockito.*;//mock creationList mockedList = mock(List.class);//using mock objectmockedList.add("one");mockedList.clear();//verificationverify(mockedList).add("one");verify(mockedList).clear();
2.stubbing
- 默认情况下,所有方法都会返回值,一个 mock 将返回要么 null,一个原始/基本类型的包装值或适当的空集。例如,对于一个 int/Integer 就是 0,而对于 boolean/Boolean 就是 false。
- Stubbing 可以被覆盖。
- 一旦 stub,该方法将始终返回一个 stub 的值,无论它有多少次被调用。
- 最后的 stubbing 是很重要的 - 当你使用相同的参数 stub 多次同样的方法。换句话说:stubbing 的顺序是重要的,但它唯一有意义的却很少,例如当 stubbing 完全相同的方法调用,或者有时当参数匹配器的使用,等等。
//You can mock concrete classes, not just interfaces LinkedList mockedList = mock(LinkedList.class); //stubbing when(mockedList.get(0)).thenReturn("first"); when(mockedList.get(1)).thenThrow(new RuntimeException()); //following prints "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 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). //If your code doesn't care what get(0) returns, then it should not be stubbed. Not convinced? See here. verify(mockedList).get(0);
3.参数匹配器
Mockito 验证参数值使用 Java 方式:通过使用 equals() 方法。有时,当需要额外的灵活性,可以使用参数匹配器
参数匹配器允许灵活的验证或 stubbing。点击 查看更多内置的匹配器和自定义的参数匹配器/ hamcrest匹配器的例子。
自定义参数的匹配信息,请查看 Javadoc 中 类。
如果你正在使用参数的匹配,所有的参数都由匹配器来提供。
//stubbing using built-in anyInt() argument matcherwhen(mockedList.get(anyInt())).thenReturn("element");//stubbing using custom matcher (let's say isValid() returns your own matcher implementation):when(mockedList.contains(argThat(isValid()))).thenReturn("element");//following prints "element"System.out.println(mockedList.get(999));//you can also verify using an argument matcherverify(mockedList).get(anyInt());
verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));//above is correct - eq() is also an argument matcherverify(mock).someMethod(anyInt(), anyString(), "third argument");//above is incorrect - exception will be thrown because third argument is given without an argument matcher.
4.调用额外的调用数字/at least x / never
//using mockmockedList.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 defaultverify(mockedList).add("once");verify(mockedList, times(1)).add("once");//exact number of invocations verificationverify(mockedList, times(2)).add("twice");verify(mockedList, times(3)).add("three times");//verification using never(). never() is an alias to times(0)verify(mockedList, never()).add("never happened");//verification using atLeast()/atMost()verify(mockedList, atLeastOnce()).add("three times");verify(mockedList, atLeast(2)).add("three times");verify(mockedList, atMost(5)).add("three times");
5.Stubbing void 方法处理异常
doThrow(new RuntimeException()).when(mockedList).clear();//following throws RuntimeException:mockedList.clear();
6.有序的验证
有序验证是为了灵活 - 你不必一个接一个验证所有的交互。
此外,您还可以通过创建 InOrder 对象传递只与有序验证相关的 mock 。
// A. Single mock whose methods must be invoked in a particular orderList singleMock = mock(List.class);//using a single mocksingleMock.add("was added first");singleMock.add("was added second");//create an inOrder verifier for a single mockInOrder 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");// B. Multiple mocks that must be used in a particular orderList firstMock = mock(List.class);List secondMock = mock(List.class);//using mocksfirstMock.add("was called first");secondMock.add("was called second");//create inOrder object passing any mocks that need to be verified in orderInOrder inOrder = inOrder(firstMock, secondMock);//following will make sure that firstMock was called before secondMockinOrder.verify(firstMock).add("was called first");inOrder.verify(secondMock).add("was called second");// Oh, and A + B can be mixed together at will
7.确保 mock 上不会发生交互
//using mocks - only mockOne is interactedmockOne.add("one");//ordinary verificationverify(mockOne).add("one");//verify that method was never called on a mockverify(mockOne, never()).add("two");//verify that other mocks were not interactedverifyZeroInteractions(mockTwo, mockThree);
8.寻找多余的调用
注意:不建议 verifyNoMoreInteractions() 在每个测试方法中使用。 verifyNoMoreInteractions() 是从交互测试工具包一个方便的断言。只有与它的相关时才使用它。滥用它导致难以维护。
//using mocksmockedList.add("one");mockedList.add("two");verify(mockedList).add("one");//following verification will failverifyNoMoreInteractions(mockedList);
9.标准创建 mock 方式 - 使用 @Mock
注解
-
最小化可重用 mock 创建代码
-
使测试类更加可读性
-
使验证错误更加易读,因为字段名称用于唯一识别 mock
public class ArticleManagerTest { @Mock private ArticleCalculator calculator; @Mock private ArticleDatabase database; @Mock private UserProvider userProvider; private ArticleManager manager;}
在基础类或者测试 runner 里面,使用如下:
MockitoAnnotations.initMocks(testClass);
可以使用内建 runner: 或者 rule:
更多详见
10. Stubbing 连续调用(迭代器式的 stubbing)
when(mock.someMethod("some arg")) .thenThrow(new RuntimeException()) .thenReturn("foo");//First call: throws runtime exception:mock.someMethod("some arg");//Second call: prints "foo"System.out.println(mock.someMethod("some arg"));//Any consecutive call: prints "foo" as well (last stubbing wins).System.out.println(mock.someMethod("some arg"));
下面是一个精简版本:
when(mock.someMethod("some arg")) .thenReturn("one", "two", "three");
11.回调 Stubbing
允许使用泛型 接口。
然而,这是不包括在最初的 Mockito 另一个有争议的功能。我们建议您只需用thenReturn() 或 thenThrow() 来 stubbing ,这在测试/测试驱动中应用简洁与简单的代码足够了。但是,如果你有一个需要 stub 到泛型 Answer 接口,这里是一个例子:
when(mock.someMethod(anyString())).thenAnswer(new Answer() { Object answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); Object mock = invocation.getMock(); return "called with arguments: " + args; }});//the following prints "called with arguments: foo"System.out.println(mock.someMethod("foo"));
12.doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod() 家族方法
Stubbing void 方法,需要不同的 when(Object) ,因为编译器不喜欢括号内无效的方法...
在 用于 Stubbing void 方法中,doThrow(Throwable...) 取代 stubVoid(Object)。主要原因是提高可读性和与 doAnswer() 保持一致性。
当你想用 stub void 方法 使用 doThrow():
doThrow(new RuntimeException()).when(mockedList).clear();//following throws RuntimeException:mockedList.clear();
在调用 when() 的相应地方可以使用 oThrow(), doAnswer(), doNothing(), doReturn() 和 doCallRealMethod(),当:
- stub void 方法
- stub 方法在 spy 对象(见下面)
- 可以不止一次的 stub 相同的方法,在测试的中期来改变 mock 的行为
但你更加倾向于使用这些方法来代替 when(),在所有的 stubbing 调用。可以阅读更多关于这些方法的描述: