<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Sky's blog

    我和我追逐的夢

    常用鏈接

    統計

    其他鏈接

    友情鏈接

    最新評論

    淺談mock和stub



        作為測試的基本概念,在開發測試中經常遇到mock和stub。之前認為自己對這兩個概念已經很明白了,但是當決定要寫下來并寫清楚以便能讓不明白的人也能弄明白,似乎就很有困難。

        試著寫下此文,以檢驗自己是不是真的明白mock和stub。

    1) 相同點
        
    先看看兩者的相同點吧,非常明確的是,mock和stub都可以用來對系統(或者將粒度放小為模塊,單元)進行隔離。

    在測試,尤其是單元測試中,我們通常關注的是主要測試對象的功能和行為,對于主要測試對象涉及到的次要對象尤其是一些依賴,我們僅僅關注主要測試對象和次要測試對象的交互,比如是否調用,何時調用,調用的參數,調用的次數和順序等,以及返回的結果或發生的異常。但次要對象是如何執行這次調用的具體細節,我們并不關注,因此常見的技巧就是用mock對象或者stub對象來替代真實的次要對象,模擬真實場景來進行對主要測試對象的測試工作。

    因此從實現上看,mock和stub都是通過創建自己的對象來替代次要測試對象,然后按照測試的需要控制這個對象的行為。

    2) 不同點

    1. 類實現的方式

    從類的實現方式上看,stub有一個顯式的類實現,按照stub類的復用層次可以實現為普通類(被多個測試案例復用),內部類(被同一個測試案例的多個測試方法復用)乃至內部匿名類(只用于當前測試方法)。對于stub的方法也會有具體的實現,哪怕簡單到只有一個簡單的return語句。

    而mock則不同,mock的實現類通常是有mock的工具包如easymock, jmock來隱式實現,具體mock的方法的行為則通過record方式來指定。

    以mock一個UserService, UserDao為例,最簡單的例子,只有一個查詢方法:

    public interface UserService {
    User query(String userId);
    }

    public class UserServiceImpl implements UserService {
    private UserDao userDao; 
    public User query(String userId) {
    return userDao.getById(userId);
    }
    //setter for userDao
    }

    public interface UserDao {
    User getById(String userId);
    }

    stub的標準實現,需要自己實現一個類并實現方法:

    public class UserDaoStub implements UserDao {
    public User getById(String id) {
    User user = new User();
    user.set.....
    return user;
    }
    }

    @Test
    public void testGetById() {
    UserServiceImpl service = new UserServiceImpl();
    UserDao userDao  = new UserDaoStub();
    service.setUserDao(userDao);

    User user = service.query("1001");
    ...
    }

    mock的實現,以easymock為例,只要指定mock的類并record期望的行為,并沒有顯式的構造新類:

    @Test
    public void testGetById() {
    UserDao dao = Easymock.createMock(UserDao.class);
    User user = new User();
    user.set.....
    Easymock.expect(dao.getById("1001")).andReturn(user);
    Easymock.reply(dao);

    UserServiceImpl service = new UserServiceImpl();
    service.setUserDao(userDao);
    User user = service.query("1001");
    ...
    Easymock.verify(dao);
    }

    對比可以看出,mock編寫相對簡單,只需要關注被使用的函數,所謂"just enough"。stub要復雜一些,需要實現邏輯,即使是不需要關注的方法也至少要給出空實現。

    2. 測試邏輯的可讀性

    從上面的代碼可以看出,在形式上,mock通常是在測試代碼中直接mock類和定義mock方法的行為,測試代碼和mock的代碼通常是放在一起的,因此測試代碼的邏輯也容易從測試案例的代碼上看出來。Easymock.expect(dao.getById("1001")).andReturn(user); 直截了當的指明了當前測試案例對UserDao這個依賴的預期: getById需要被調用,調用的參數應該是"1001",調用次數為1(不明確指定調用次數時easymock默認為1)。

    而stub的測試案例的代碼中只有簡單的UserDao userDao  = new UserDaoStub ();構造語句和service.setUserDao(userDao);設置語句,我們無法直接從測試案例的代碼中看出對依賴的預期,只能進入具體的UserServiceImpl類的query()方法,看到具體的實現是調用userDao.getById(userId),這個時候才能明白完整的測試邏輯。因此當測試邏輯復雜,stub數量多并且某些stub需要傳入一些標記比如true,false之類的來制定不同的行為時,測試邏輯的可讀性就會下降。

    3. 可復用性
    Mock通常很少考慮復用,每個mock對象通過都是遵循"just enough"原則,一般只適用于當前測試方法。因此每個測試方法都必須實現自己的mock邏輯,當然在同一個測試類中還是可以有一些簡單的初始化邏輯可以復用。

    stub則通常比較方便復用,尤其是一些通用的stub,比如jdbc連接之類。spring框架就為此提供了大量的stub來方便測試,不過很遺憾的是,它的名字用錯了:spring-mock!

    4. 設計和使用

    接著我們從mock和stub的設計和使用上來比較兩者,這里需要引入兩個概念:interaction-based和state-based。

    具體關于interaction-based和state-based,不再本文闡述,強烈推薦Martin Fowler 的一篇文章,"Mocks Aren't Stubs"。地址為http://martinfowler.com/articles/mocksArentStubs.html(PS:當在google中輸入mock stub兩個關鍵字做搜索時,出來結果的第一條就是此文,向Martin Fowler致敬,向google致敬),英文不好的同學,可以參考這里的一份中文翻譯:http://www.cnblogs.com/anf/archive/2006/03/27/360248.html。

    總結來說,stub是state-based,關注的是輸入和輸出。mock是interaction-based,關注的是交互過程。

    5. expectiation/期望

    這個才是mock和stub的最重要的區別:expectiation/期望。

    對于mock來說,exception是重中之重:我們期待方法有沒有被調用,期待適當的參數,期待調用的次數,甚至期待多個mock之間的調用順序。所有的一切期待都是事先準備好,在測試過程中和測試結束后驗證是否和預期的一致。

    而對于stub,通常都不會關注exception,就像上面給出的UserDaoStub的例子,沒有任何代碼來幫助判斷這個stub類是否被調用。雖然理論上某些stub實現也可以通過自己編碼的方式增加對expectiation的內容,比如增加一個計數器,每次調用+1之類,但是實際上極少這樣做。 

    6. 總結

    關于mock和stub的不同,在Martin Fowler的"Mocks Aren't Stubs"一文中,有以下結束,我將它列出來作為總結:

    (1) Dummy 
    對象被四處傳遞,但是從不被真正使用。通常他們只是用來填充參數列表。

    (2) Fake 
    有實際可工作的實現,但是通常有一些缺點導致不適合用于產品(基于內存的數據庫就是一個好例子)。

    (3) Stubs 

    在測試過程中產生的調用提供預備好的應答,通常不應答計劃之外的任何事。stubs可能記錄關于調用的信息,比如 郵件網關的stub 會記錄它發送的消息,或者可能僅僅是發送了多少信息。

    (4) Mocks 

    如我們在這里說的那樣:預先計劃好的對象,帶有各種期待,他們組成了一個關于他們期待接受的調用的詳細說明。

    3) 退化和轉化

    在實際的開發測試過程中,我們會發現其實mock和stub的界限有時候很模糊,并沒有嚴格的劃分方式,從而造成我們理解上的含糊和困惑。

    主要的原因在于現實使用中,我們經常將mock做不同程度的退化,從而使得mock對象在某些程度上如stub一樣工作。以easymock為例,我們可以通過anyObject(), isA(Class)等方式放寬對參數的檢測,以atLeatOnce(),anytimes()來放松對調用次數的檢測,我們可以使用Easymock.createControl()而不是Easymock.createStrictControl()來放寬對調用順序的檢測(或者調用checkOrder(false)),我們甚至可以通過createNiceControl(), createNiceMock()來創建完全不限制調用方式而且自動返回簡單值的mock,這和stub就幾乎沒有本質區別了。

    目前大多數的mock工具都提供mock退化為stub的支持,比如easyock中,除了上面列出的any***,NiceMock之外,還提供諸如andStubAnswer(),andStubDelegateTo(),andStubReturn(),andStubThrow()和asStub()。

    上面也談到過stub也是可以通過增加代碼來實現一些expectiation的特性,stub理論上也是可以向mock的方向做轉化,而從使得兩者的界限更加的模糊。

    posted on 2010-08-26 15:28 sky ao 閱讀(10691) 評論(4)  編輯  收藏 所屬分類: software test

    評論

    # re: 淺談mock和stub 2010-08-26 17:31 bingo

    非常不錯!受教了!  回復  更多評論   

    # re: 淺談mock和stub 2012-12-21 13:44 eccentric

    寫的很棒,感激不盡,這個系列也很棒,希望博主繼續。  回復  更多評論   

    # re: 淺談mock和stub[未登錄] 2013-01-08 15:53 vanessa

    寫的非常好,看了就忍不住想去試用一下,博主加油!  回復  更多評論   

    # re: 淺談mock和stub[未登錄] 2016-04-26 11:50 jimmy

    寫的清楚,支持下  回復  更多評論   

    主站蜘蛛池模板: 2017亚洲男人天堂一| 亚洲三级高清免费| 国产黄片不卡免费| 亚洲一区二区高清| 国产精品免费看久久久香蕉| 亚洲午夜国产精品无码| 中文字幕乱码系列免费| 亚洲AV成人片色在线观看高潮| 无码人妻一区二区三区免费看| 91精品国产亚洲爽啪在线观看| 国产91色综合久久免费分享| 亚洲中文字幕一二三四区| 国产免费观看视频| 国产中文字幕在线免费观看| 亚洲成aⅴ人片久青草影院按摩 | 99久久精品毛片免费播放| 亚洲日产无码中文字幕| 亚洲午夜免费视频| 亚洲国产精品成人精品软件| 女人让男人免费桶爽30分钟| 日本在线观看免费高清| 久久亚洲精品中文字幕三区| 最近中文字幕mv免费高清在线| 亚洲欧洲日韩极速播放| 又爽又高潮的BB视频免费看| 国产成年无码久久久免费| 亚洲国产精品网站久久| 凹凸精品视频分类国产品免费| 美女无遮挡拍拍拍免费视频| 99久久精品国产亚洲| 日本无吗免费一二区| 免费无码av片在线观看| 亚洲人成小说网站色| 国产亚洲美女精品久久久| 青青草a免费线观a| 国产精品1024在线永久免费| 亚洲精品视频免费看| 波多野结衣视频在线免费观看| 日本亚洲欧洲免费天堂午夜看片女人员 | 免费又黄又爽的视频| 99视频在线精品免费|