什么是mock?
在軟件開發的世界之外, "mock"一詞是指模仿或者效仿。 因此可以將“mock”理解為一個替身,替代者. 在軟件開發中提及"mock",通常理解為模擬對象或者Fake。
為什么需要Mock?
Mock是為了解決units之間由于耦合而難于被測試的問題。所以mock object是unit test的一部分。
提前創建測試,TDD(測試驅動開發)
這是個最大的好處吧。如果你創建了一個Mock那么你就可以在service接口創建之前寫Service Tests了,這樣你就能在開發過程中把測試添加到你的自動化測試環境中了。換句話說,模擬使你能夠使用測試驅動開發。
團隊可以并行工作
這類似于上面的那點;為不存在的代碼創建測試。但前面講的是開發人員編寫測試程序,這里說的是測試團隊來創建。當還沒有任何東西要測的時候測試團隊如何來創建測試呢?模擬并針對模擬測試!這意味著當service借口需要測試時,實際上QA團隊已經有了一套完整的測試組件;沒有出現一個團隊等待另一個團隊完成的情況。這使得模擬的效益型尤為突出了。
你可以創建一個驗證或者演示程序。
由于Mocks非常高效,Mocks可以用來創建一個概念證明,作為一個示意圖,或者作為一個你正考慮構建項目的演示程序。這為你決定項目接下來是否要進行提供了有力的基礎,但最重要的還是提供了實際的設計決策。
為無法訪問的資源編寫測試
這個好處不屬于實際效益的一種,而是作為一個必要時的“救生圈”。有沒有遇到這樣的情況?當你想要測試一個service接口,但service需要經過防火墻訪問,防火墻不能為你打開或者你需要認證才能訪問。遇到這樣情況時,你可以在你能訪問的地方使用MockService替代,這就是一個“救生圈”功能。
Mock 可以交給用戶
在有些情況下,某種原因你需要允許一些外部來源訪問你的測試系統,像合作伙伴或者客戶。這些原因導致別人也可以訪問你的敏感信息,而你或許只是想允許訪問部分測試環境。在這種情況下,如何向合作伙伴或者客戶提供一個測試系統來開發或者做測試呢?最簡單的就是提供一個mock,無論是來自于你的網絡或者客戶的網絡。soapUI mock非常容易配置,他可以運行在soapUI或者作為一個war包發布到你的java服務器里面。
隔離系統
有時,你希望在沒有系統其他部分的影響下測試系統單獨的一部分。由于其他系統部分會給測試數據造成干擾,影響根據數據收集得到的測試結論。使用mock你可以移除掉除了需要測試部分的系統依賴的模擬。當隔離這些mocks后,mocks就變得非常簡單可靠,快速可預見。這為你提供了一個移除了隨機行為,有重復模式并且可以監控特殊系統的測試環境。
// 模擬LinkedList 的一個對象
LinkedList mockedList = mock(LinkedList.class);
// 此時調用get方法,會返回null,因為還沒有對方法調用的返回值做模擬
System.out.println(mockedList.get(0));
// 模擬獲取第一個元素時,返回字符串first。 給特定的方法調用返回固定值在官方說法中稱為stub。
when(mockedList.get(0)).thenReturn("first");
// 此時打印輸出first
System.out.println(mockedList.get(0));
// 模擬獲取第二個元素時,拋出RuntimeException
when(mockedList.get(1)).thenThrow(new RuntimeException());
// 此時將會拋出RuntimeException
System.out.println(mockedList.get(1));
如果一個函數沒有返回值類型,那么可以使用此方法模擬異常拋出
doThrow(new RuntimeException("clear exception")).when(mockedList).clear();
mockedList.clear();
// anyInt()匹配任何int參數,這意味著參數為任意值,其返回值均是element
when(mockedList.get(anyInt())).thenReturn("element");
// 此時打印是element
System.out.println(mockedList.get(999));
// 調用add一次
mockedList.add("once");
// 下面兩個寫法驗證效果一樣,均驗證add方法是否被調用了一次
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
// mock creation
List mockedList = mock(List.class);
// using mock object
mockedList.add("one");
mockedList.clear();
//verification
verify(mockedList).add("one");
verify(mockedList).clear();
//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));
verify(mockedList).get(0);
//stubbing using built-in anyInt() argument matcher
when(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 matcher
verify(mockedList).get(anyInt());
//argument matchers can also be written as Java 8 Lambdas
verify(mockedList).add(someString -> someString.length() > 5);
//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
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)
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");
doThrow(new RuntimeException()).when(mockedList).clear();
//following throws RuntimeException:
mockedList.clear();
// A. Single mock whose methods must be invoked in a particular order
List singleMock = mock(List.class);
//using a single mock
singleMock.add("was added first");
singleMock.add("was added second");
//create an inOrder verifier for a single mock
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");
// B. Multiple mocks that must be used in a particular order
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
InOrder inOrder = inOrder(firstMock, secondMock);
//following will make sure that firstMock was called before 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
//using mocks - only mockOne is interacted
mockOne.add("one");
//ordinary verification
verify(mockOne).add("one");
//verify that method was never called on a mock
verify(mockOne, never()).add("two");
//verify that other mocks were not interacted
verifyZeroInteractions(mockTwo, mockThree);
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock private ArticleDatabase database;
@Mock private UserProvider userProvider;
@Before
public void before(){
MockitoAnnotations.initMocks(this);
}
}
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")); // 第n次(n> 2),依舊以最后返回最后一個配置
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"));
可以通過doThrow(), doAnswer(), doNothing(), doReturn() and doCallRealMethod() 來自定義函數操作。
List list = new LinkedList();
List spy = spy(list);
//optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);
//using the spy calls *real* methods
spy.add("one");
spy.add("two");
//prints "one" - the first element of a list
System.out.println(spy.get(0));
//size() method was stubbed - 100 is printed
System.out.println(spy.size());
//optionally, you can verify
verify(spy).add("one");
verify(spy).add("two");
Foo mock = mock(Foo.class, Mockito.RETURNS_SMART_NULLS);
Foo mockTwo = mock(Foo.class, new YourOwnAnswer());
ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());
//you can create partial mock with spy() method:
List list = spy(new LinkedList());
//you can enable partial mock capabilities selectively on mocks:
Foo mock = mock(Foo.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.someMethod()).thenCallRealMethod();
List mock = mock(List.class);
when(mock.size()).thenReturn(10);
mock.add(1);
reset(mock);
//at this point the mock forgot any interactions & stubbing
List<Object> list = new ArrayList<Object>();
List<Object> spy = mock(ArrayList.class, withSettings()
.spiedInstance(list)
.defaultAnswer(CALLS_REAL_METHODS)
.serializable());
//passes when someMethod() is called within given time span
verify(mock, timeout(100)).someMethod();
//above is an alias to:
verify(mock, timeout(100).times(1)).someMethod();
//passes when som`eMethod() is called *exactly* 2 times within given time span
verify(mock, timeout(100).times(2)).someMethod();
//passes when someMethod() is called *at least* 2 times within given time span
verify(mock, timeout(100).atLeast(2)).someMethod();
//verifies someMethod() within given time span using given verification mode
//useful only if you have your own custom verification modes.
verify(mock, new Timeout(100, yourOwnVerificationMode)).someMethod();
Mockito.mockingDetails(someObject).isMock();
Mockito.mockingDetails(someObject).isSpy();
原文轉自:http://www.jianshu.com/p/77db26b4fb54