LinkedListmockedList=mock(LinkedList.class);//stubbingwhen(mockedList.get(0)).thenReturn("first");when(mockedList.get(1)).thenThrow(newRuntimeException());//following prints "first"System.out.println(mockedList.get(0));//following throws runtime exceptionSystem.out.println(mockedList.get(1));//following prints "null" because get(999) was not stubbedSystem.out.println(mockedList.get(999));
这里指定关键字 when 返回一个 OngoingStubbing 接口,通过其提供的 thenReturn,thenThrow,thenCallRealMethod 及自定义 thenAnswer 来返回相应的结果。
3.参数匹配
123456789
LinkedListmockedList=mock(LinkedList.class);//stubbing using built-in anyInt() argument matcherwhen(mockedList.get(anyInt())).thenReturn("element");//following prints "element"System.out.println(mockedList.get(999));//you can also verify using an argument matcherverify(mockedList).get(anyInt());
有时我们针对函数参数的模拟,不是一个特定的数值,而是一个范围。这时可以范围型的参数匹配,在 ArgumentMatchers 中,提供了一组不同类型的 any 操作。如:any(Class),anyObject(),anyVararg(),anyChar(),anyInt(),anyBoolean(),anyCollectionOf(Class)等。
4.调用次数
123456789101112131415161718192021222324252627
LinkedListmockedList=mock(LinkedList.class);//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("twice");verify(mockedList,atMost(5)).add("three times");
// A. Single mock whose methods must be invoked in a particular orderListsingleMock=mock(List.class);//using a single mocksingleMock.add("was added first");singleMock.add("was added second");//create an inOrder verifier for a single mockInOrderinOrder1=inOrder(singleMock);//following will make sure that add is first called with "was added first, then with "was added second"inOrder1.verify(singleMock).add("was added first");inOrder1.verify(singleMock).add("was added second");// B. Multiple mocks that must be used in a particular orderListfirstMock=mock(List.class);ListsecondMock=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 orderinOrder1=inOrder(firstMock,secondMock);//following will make sure that firstMock was called before secondMockinOrder1.verify(firstMock).add("was called first");inOrder1.verify(secondMock).add("was called second");
ListmockOne=mock(List.class);ListmockTwo=mock(List.class);ListmockThree=mock(List.class);//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);
通过 never 来指定一个方法从未发生调用,使用 verifyZeroInteractions 来确定对象的实例从未发生调用
8. 没有更多调用
12345678910
ListmockedList=mock(List.class);//using mocksmockedList.add("one");mockedList.add("two");verify(mockedList).add("one");//following verification will failverifyNoMoreInteractions(mockedList);
HashMapmock=mock(HashMap.class);when(mock.get(anyString())).thenAnswer(newAnswer<Object>(){@OverridepublicObjectanswer(InvocationOnMockinvocation)throwsThrowable{Object[]args=invocation.getArguments();Objectmock=invocation.getMock();return"called with arguments: "+args[0];}});//the following prints "called with arguments: foo"System.out.println(mock.get("foo"));
当我们一个函数方法返回结果的不确定性,需要动态地根据参数指来改变。则上述的几个 then 方法不满足的情况下,我们可以通过 thenAnswer 方法返回一个 Answer 对象,来动态地返回结果。
Listlist=newLinkedList();ListsypList=spy(list);//optionally, you can stub out some methods:when(sypList.size()).thenReturn(100);//using the spy calls *real* methodssypList.add("one");sypList.add("two");//prints "one" - the first element of a listSystem.out.println(sypList.get(0));//size() method was stubbed - 100 is printedSystem.out.println(sypList.size());//optionally, you can verifyverify(sypList).add("one");verify(sypList).add("two");
Listlist=newLinkedList();Listspy=spy(list);//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)//when(spy.get(0)).thenReturn("foo");//You have to use doReturn() for stubbingdoReturn("foo").when(spy).get(0);
使用 when + thenReturn ,并不返回我们预期的结果,而是需要使用 doReturn + when 的格式。
其原因在于,Mockito 框架并不会对真实的对象进行 mock,只会真实的对象创建一个副本。
classListOfTwoElementsimplementsArgumentMatcher<List>{publicbooleanmatches(Listlist){returnlist.size()==2;}publicStringtoString(){//printed in verification errorsreturn"[list of 2 elements]";}}Listmock=mock(List.class);when(mock.addAll(argThat(newListOfTwoElements()))).thenReturn(true);mock.addAll(Arrays.asList("one","two"));verify(mock).addAll(argThat(newListOfTwoElements()));
实现 ArgumentMatcher 类,并通过 argThat 方法对参数进行判断。
16.对真实类的部分 mock
这里一般有两种写法:
1) 使用 spy
12345678910
@TestpublicvoidtestPartialRealMock1(){//you can create partial mock with spy() method:LinkedListlinkedList=newLinkedList();linkedList.addFirst(1);Listlist=spy(linkedList);assertThat(list.get(0),is(1));}
通过 spy 调用对象的方法,将会调用其真正的方法。
2) 使用 mock
12345678910111213141516
@RulepublicExpectedExceptionthrown=ExpectedException.none();@TestpublicvoidtestPartialRealMock2(){//you can enable partial mock capabilities selectively on mocks:Listmock=mock(LinkedList.class);//Be sure the real implementation is 'safe'.//If real implementation throws exceptions or depends on specific state of the object then you're in trouble.when(mock.get(anyInt())).thenCallRealMethod();thrown.expect(Exception.class);mock.get(0);}
针对 mock 的使用时,主要代码在于方法 thenCallRealMethod(),但它有个很大的安全隐患,就是此方法抛出异常的问题。上述代码就可以看出,因为真实的 list 对象,并不含有任何元素,所以在通过真实方法返回时,就会有异常产生。
Listmock=mock(List.class);when(mock.get(0)).thenReturn(1);System.out.println(mock.get(0));verify(mock,timeout(100)).get(0);//above is an alias to:verify(mock,timeout(100).times(1)).get(0);System.out.println(mock.get(0));verify(mock,timeout(100).times(2)).get(0);verify(mock,timeout(100).atLeast(2)).get(0);verify(mock,newTimeout(100,newVerificationMode(){@Overridepublicvoidverify(VerificationDatadata){}@OverridepublicVerificationModedescription(Stringdescription){returnnull;}})).get(0);
//mocking lists for the sake of the example (if you mock List in real you will burn in hell)Listmock1=mock(List.class),mock2=mock(List.class);//stubbing mocks:when(mock1.get(0)).thenReturn(10);when(mock2.get(0)).thenReturn(20);//using mocks by calling stubbed get(0) methods://System.out.println(mock1.get(0)); //prints 10System.out.println(mock2.get(0));//prints 20mock1.get(0);verify(mock1).get(0);//using mocks by calling clear() methods:mock1.clear();mock2.clear();//verification:verify(mock1).clear();verify(mock2).clear();//verifyNoMoreInteractions() fails because get() methods were not accounted for.try{verifyNoMoreInteractions(mock1,mock2);}catch(NoInteractionsWantede){System.out.println(e);}//However, if we ignore stubbed methods then we can verifyNoMoreInteractions()verifyNoMoreInteractions(ignoreStubs(mock1,mock2));
Listlist=mock(List.class);when(list.get(0)).thenReturn("foo");list.add(0);System.out.println(list.get(0));//we don't want to verify thislist.clear();//verify(list).add(0);//verify(list).add(0);//verify(list).clear();// Same as: InOrder inOrder = inOrder(list);InOrderinOrder=inOrder(ignoreStubs(list));inOrder.verify(list).add(0);// this will have an error..//inOrder.verify(list).get(0);inOrder.verify(list).clear();inOrder.verifyNoMoreInteractions();