Mockit:測試框架基礎使用

  • 什麼是mock?
    在軟件開發的世界以外, "mock"一詞是指模仿或者效仿。 所以能夠將「mock」理解爲一個替身,替代者. 在軟件開發中說起"mock",一般理解爲模擬對象或者Fake。
  • 爲何須要Mock?
    Mock是爲了解決units之間因爲耦合而難於被測試的問題。因此mock object是unit test的一部分。
  • Mock的好處是什麼?app

    • 提早建立測試,TDD(測試驅動開發)
      若是你建立了一個Mock那麼你就能夠在service接口建立以前寫Service Tests了,這樣你就能在開發過程當中把測試添加到你的自動化測試環境中了。換句話說,模擬使你可以使用測試驅動開發。
    • 團隊能夠並行工做
      這相似於上面的那點;爲不存在的代碼建立測試。但前面講的是開發人員編寫測試程序,這裏說的是測試團隊來建立。當尚未任何東西要測的時候測試團隊如何來建立測試呢?模擬並針對模擬測試!這意味着當service藉口須要測試時,實際上QA團隊已經有了一套完整的測試組件;沒有出現一個團隊等待另外一個團隊完成的狀況。這使得模擬的效益型尤其突出了。
    • 你能夠建立一個驗證或者演示程序。
      因爲Mocks很是高效,Mocks能夠用來建立一個概念證實,做爲一個示意圖,或者做爲一個你正考慮構建項目的演示程序。這爲你決定項目接下來是否要進行提供了有力的基礎,但最重要的仍是提供了實際的設計決策。
    • 隔離系統
      有時,你但願在沒有系統其餘部分的影響下測試系統單獨的一部分。因爲其餘系統部分會給測試數據形成干擾,影響根據數據收集獲得的測試結論。使用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() 來自定義函數操做。ide

暗中調用真實對象函數

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();
相關文章
相關標籤/搜索