TDD(測試驅動開發)中Mockito使用淺談

發表于:2016-07-21來源:jianshu作者:流水不腐小夏點擊數: 標簽:tdd
在軟件開發的世界之外, "mock"一詞是指模仿或者效仿。 因此可以將“mock”理解為一個替身,替代者. 在軟件開發中提及"mock",通常理解為模擬對象或者Fake。

Mockito簡介

  • 什么是mock?
    在軟件開發的世界之外, "mock"一詞是指模仿或者效仿。 因此可以將“mock”理解為一個替身,替代者. 在軟件開發中提及"mock",通常理解為模擬對象或者Fake。

  • 為什么需要Mock?
    Mock是為了解決units之間由于耦合而難于被測試的問題。所以mock object是unit test的一部分。

Mock的好處是什么?

  • 提前創建測試,TDD(測試驅動開發)

    這是個最大的好處吧。如果你創建了一個Mock那么你就可以在service接口創建之前寫Service Tests了,這樣你就能在開發過程中把測試添加到你的自動化測試環境中了。換句話說,模擬使你能夠使用測試驅動開發。

  • 團隊可以并行工作

    這類似于上面的那點;為不存在的代碼創建測試。但前面講的是開發人員編寫測試程序,這里說的是測試團隊來創建。當還沒有任何東西要測的時候測試團隊如何來創建測試呢?模擬并針對模擬測試!這意味著當service借口需要測試時,實際上QA團隊已經有了一套完整的測試組件;沒有出現一個團隊等待另一個團隊完成的情況。這使得模擬的效益型尤為突出了。

  • 你可以創建一個驗證或者演示程序。

    由于Mocks非常高效,Mocks可以用來創建一個概念證明,作為一個示意圖,或者作為一個你正考慮構建項目的演示程序。這為你決定項目接下來是否要進行提供了有力的基礎,但最重要的還是提供了實際的設計決策。

  • 為無法訪問的資源編寫測試

    這個好處不屬于實際效益的一種,而是作為一個必要時的“救生圈”。有沒有遇到這樣的情況?當你想要測試一個service接口,但service需要經過防火墻訪問,防火墻不能為你打開或者你需要認證才能訪問。遇到這樣情況時,你可以在你能訪問的地方使用MockService替代,這就是一個“救生圈”功能。

  • Mock 可以交給用戶

    在有些情況下,某種原因你需要允許一些外部來源訪問你的測試系統,像合作伙伴或者客戶。這些原因導致別人也可以訪問你的敏感信息,而你或許只是想允許訪問部分測試環境。在這種情況下,如何向合作伙伴或者客戶提供一個測試系統來開發或者做測試呢?最簡單的就是提供一個mock,無論是來自于你的網絡或者客戶的網絡。soapUI mock非常容易配置,他可以運行在soapUI或者作為一個war包發布到你的java服務器里面。

  • 隔離系統

    有時,你希望在沒有系統其他部分的影響下測試系統單獨的一部分。由于其他系統部分會給測試數據造成干擾,影響根據數據收集得到的測試結論。使用mock你可以移除掉除了需要測試部分的系統依賴的模擬。當隔離這些mocks后,mocks就變得非常簡單可靠,快速可預見。這為你提供了一個移除了隨機行為,有重復模式并且可以監控特殊系統的測試環境。

Mockito使用示例

模擬對象

// 模擬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();

模擬方法調用(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));

 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);

快速創建Mock對象

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"));

Mock函數操作

可以通過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());

部分Mock

//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();

重置Mock

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();

Mock詳情

Mockito.mockingDetails(someObject).isMock();
Mockito.mockingDetails(someObject).isSpy();

 

原文轉自:http://www.jianshu.com/p/77db26b4fb54

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97