1. 程式人生 > >說說初用 Mock 工具測試碰到的坑

說說初用 Mock 工具測試碰到的坑

我是一個在校實習生,作為一個程式猿,是個菜鳥中戰鬥機!對於測試,只寫過一點點簡單到不能再簡單了的 Junit 單元測試的例子(因為當時這足以應付學校課程的內容與要求)。這幾天在公司裡要真槍實彈做測試的時候,就深深體會到了“書到用時方恨少”這句話的真諦了。沒辦法,誰叫我當初不多深入的學點呢。於是,在杜叔杜大神的指導下,開始了菜鳥的初用 Mock 工具,邊學邊用的爬坑之旅。

為什麼要用 Mock 工具?

話不多說,我們先直接看看個最簡單的例子:
測試目標:

public class ToBeTested {

    public int add(int a, int b) {
        return  a+b;
    }
    public int plus(int a, int b){
 return a*b;  } } 

測試用例:

 public class test {

    @Test
    public void testEquals(){
        ToBeTested t = new ToBeTested();
        assertEquals(3,t.add(1,2));
 assertEquals(8,t.plus(2,4));  } } 

這裡我們是直接通過 new 來構建了一個 ToBeTested 的例項,因為這個類簡單,而且待測試的方法裡也沒有依賴任何外部的物件,就一個簡單的加法或乘法就完事了。但事情總是沒有那麼簡單,在做單元測試的時候,我們要測試的方法往往都是需要依賴很多外部的物件,比如網路通訊,遠端服務之類的,這些外部物件是我麼沒法控制的。難道這些外部依賴的物件都需要 new 一個出來嗎 NO! 我們有 Mock! 我們可以用 Mock 工具來模擬這些外部物件,來完成我們的單元測試。還是先來看一個簡單的 Mock 測試的例子吧:

public class test {

    @Test
    public void test(){
        List<String> list = Mockito.mock(List.class);
        list.add("coding");
 verify(list).add("coding");  } } 

坑1 : spy

如果要用真實物件(而不是 Mock 出來的虛擬物件)中的真實的方法,則需要 spy 一下!看下面的例子:

我在測試目的碼中加了一個 compute 函式:

public int compute(int a){

        int b = add(a,a);
        int c = plus(a,a);
        return b+c;
}

隨後想當然的寫了個對應的測試用例:

@Test

    public void testCompute(){
        ToBeTested t = new ToBeTested();
        assertEquals(8, t.compute(2));
    }

但聰明的你一看就知道,這不叫單元測試。因為我只是想測試 compute 這個函式是不是正確的,但 compute 卻依賴於 add 和 plus 這兩個函式。如果 add 函式裡有錯,也會導致 compute 出錯,可這根本不關 compute 的事啊。所以,測試用例程式碼應該是這樣的:

@Test

    public void testCompute(){
        ToBeTested t = spy(new ToBeTested());
        when(t.add(2,2)).thenReturn(4);
        when(t.plus(2, 2)).thenReturn(4);
 assertEquals(8, t.compute(2));  } 

注意!這裡 new 一個真實物件 t 的時候,需要 spy 一下!(當時年輕不懂事,被這裡卡了略久。。)因為這裡用到了依賴的方法 add 和 plus。現在對於這個例子,compute 的結果已經不依賴別人了,哪怕 add 方法裡,不知是哪個粗心的程式猿寫成了 “return a - b ",這個測試也是通過的,因為這裡有 when(t.add(2,2)).thenReturn(4); 這句話,stubbing 了 add 這個方法。這也符合單元測試的概念,我們現在只負責測試 compute 這個函式,才不管 add 或 plus 正確與否呢。

坑2:doReturn

等等,如剛剛所說,compute 已經不依賴 add 的返回結果了,那是不是當我們測試 compute 函式的時候,add 函式是不是就可以完全無法無天的亂來了呢?我們來看看這個:

public int add(int a, int b) {

        ArrayList<String> list = new ArrayList<String>();
        String s = list.get(0);
        return  a+b;
}

這時,當我們再次執行測試時,就拋異常了:java.lang.IndexOutOfBoundsException 。顯然 list.get(0) 是罪魁禍首!奇怪?不是說 compute 已經不管 add 了嗎? add 裡面的程式碼有問題又怪我咯?別忘了,這還是 java 語言!所以,when(t.add(2,2)).thenReturn(4); 這語句還是會先去執行一遍 when 裡面的函式, add(2,2). 至於返回結果是另外一回事。那麼怎麼解決這個問題呢?對!用 doReturn 語句!

 @Test

    public void testCompute(){
        ToBeTested t = spy(new ToBeTested());
        //when(t.add(2,2)).thenReturn(4);
        doReturn(4).when(t).add(2,2);
 when(t.plus(2, 2)).thenReturn(4); ;  assertEquals(8, t.compute(2));  } 

現在好了, compute 已經完全不受 add 函式的影響了。add 函式愛怎麼瘋就怎麼瘋,不會影響 compute 函式的測試結果了,至此, compute 終於可以安靜地完成它的單元測試了。

坑3: PowerMock

現如今比較流行的 Mock 工具如 jMock、EasyMock、Mockito 等都有一個共同的缺點:不能 mock 靜態、final、私有方法等。而 PowerMock 能夠完美的彌補以上三個 Mock 工具的不足。至於怎麼做,去 Google 一下一大把的教程與例項,這裡就不再多說了。。

坑4,5,6,8,。。。。。

慢慢踩 :)

from: https://blog.coding.net/blog/mock-testing-tools