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

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

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

    Sky's blog

    我和我追逐的夢(mèng)

    常用鏈接

    統(tǒng)計(jì)

    其他鏈接

    友情鏈接

    最新評(píng)論

    淺談mock和stub



        作為測(cè)試的基本概念,在開發(fā)測(cè)試中經(jīng)常遇到mock和stub。之前認(rèn)為自己對(duì)這兩個(gè)概念已經(jīng)很明白了,但是當(dāng)決定要寫下來(lái)并寫清楚以便能讓不明白的人也能弄明白,似乎就很有困難。

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

    1) 相同點(diǎn)
        
    先看看兩者的相同點(diǎn)吧,非常明確的是,mock和stub都可以用來(lái)對(duì)系統(tǒng)(或者將粒度放小為模塊,單元)進(jìn)行隔離。

    在測(cè)試,尤其是單元測(cè)試中,我們通常關(guān)注的是主要測(cè)試對(duì)象的功能和行為,對(duì)于主要測(cè)試對(duì)象涉及到的次要對(duì)象尤其是一些依賴,我們僅僅關(guān)注主要測(cè)試對(duì)象和次要測(cè)試對(duì)象的交互,比如是否調(diào)用,何時(shí)調(diào)用,調(diào)用的參數(shù),調(diào)用的次數(shù)和順序等,以及返回的結(jié)果或發(fā)生的異常。但次要對(duì)象是如何執(zhí)行這次調(diào)用的具體細(xì)節(jié),我們并不關(guān)注,因此常見的技巧就是用mock對(duì)象或者stub對(duì)象來(lái)替代真實(shí)的次要對(duì)象,模擬真實(shí)場(chǎng)景來(lái)進(jìn)行對(duì)主要測(cè)試對(duì)象的測(cè)試工作。

    因此從實(shí)現(xiàn)上看,mock和stub都是通過創(chuàng)建自己的對(duì)象來(lái)替代次要測(cè)試對(duì)象,然后按照測(cè)試的需要控制這個(gè)對(duì)象的行為。

    2) 不同點(diǎn)

    1. 類實(shí)現(xiàn)的方式

    從類的實(shí)現(xiàn)方式上看,stub有一個(gè)顯式的類實(shí)現(xiàn),按照stub類的復(fù)用層次可以實(shí)現(xiàn)為普通類(被多個(gè)測(cè)試案例復(fù)用),內(nèi)部類(被同一個(gè)測(cè)試案例的多個(gè)測(cè)試方法復(fù)用)乃至內(nèi)部匿名類(只用于當(dāng)前測(cè)試方法)。對(duì)于stub的方法也會(huì)有具體的實(shí)現(xiàn),哪怕簡(jiǎn)單到只有一個(gè)簡(jiǎn)單的return語(yǔ)句。

    而mock則不同,mock的實(shí)現(xiàn)類通常是有mock的工具包如easymock, jmock來(lái)隱式實(shí)現(xiàn),具體mock的方法的行為則通過record方式來(lái)指定。

    以mock一個(gè)UserService, UserDao為例,最簡(jiǎn)單的例子,只有一個(gè)查詢方法:

    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的標(biāo)準(zhǔn)實(shí)現(xiàn),需要自己實(shí)現(xiàn)一個(gè)類并實(shí)現(xiàn)方法:

    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的實(shí)現(xiàn),以easymock為例,只要指定mock的類并record期望的行為,并沒有顯式的構(gòu)造新類:

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

    對(duì)比可以看出,mock編寫相對(duì)簡(jiǎn)單,只需要關(guān)注被使用的函數(shù),所謂"just enough"。stub要復(fù)雜一些,需要實(shí)現(xiàn)邏輯,即使是不需要關(guān)注的方法也至少要給出空實(shí)現(xiàn)。

    2. 測(cè)試邏輯的可讀性

    從上面的代碼可以看出,在形式上,mock通常是在測(cè)試代碼中直接mock類和定義mock方法的行為,測(cè)試代碼和mock的代碼通常是放在一起的,因此測(cè)試代碼的邏輯也容易從測(cè)試案例的代碼上看出來(lái)。Easymock.expect(dao.getById("1001")).andReturn(user); 直截了當(dāng)?shù)闹该髁水?dāng)前測(cè)試案例對(duì)UserDao這個(gè)依賴的預(yù)期: getById需要被調(diào)用,調(diào)用的參數(shù)應(yīng)該是"1001",調(diào)用次數(shù)為1(不明確指定調(diào)用次數(shù)時(shí)easymock默認(rèn)為1)。

    而stub的測(cè)試案例的代碼中只有簡(jiǎn)單的UserDao userDao  = new UserDaoStub ();構(gòu)造語(yǔ)句和service.setUserDao(userDao);設(shè)置語(yǔ)句,我們無(wú)法直接從測(cè)試案例的代碼中看出對(duì)依賴的預(yù)期,只能進(jìn)入具體的UserServiceImpl類的query()方法,看到具體的實(shí)現(xiàn)是調(diào)用userDao.getById(userId),這個(gè)時(shí)候才能明白完整的測(cè)試邏輯。因此當(dāng)測(cè)試邏輯復(fù)雜,stub數(shù)量多并且某些stub需要傳入一些標(biāo)記比如true,false之類的來(lái)制定不同的行為時(shí),測(cè)試邏輯的可讀性就會(huì)下降。

    3. 可復(fù)用性
    Mock通常很少考慮復(fù)用,每個(gè)mock對(duì)象通過都是遵循"just enough"原則,一般只適用于當(dāng)前測(cè)試方法。因此每個(gè)測(cè)試方法都必須實(shí)現(xiàn)自己的mock邏輯,當(dāng)然在同一個(gè)測(cè)試類中還是可以有一些簡(jiǎn)單的初始化邏輯可以復(fù)用。

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

    4. 設(shè)計(jì)和使用

    接著我們從mock和stub的設(shè)計(jì)和使用上來(lái)比較兩者,這里需要引入兩個(gè)概念:interaction-based和state-based。

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

    總結(jié)來(lái)說(shuō),stub是state-based,關(guān)注的是輸入和輸出。mock是interaction-based,關(guān)注的是交互過程。

    5. expectiation/期望

    這個(gè)才是mock和stub的最重要的區(qū)別:expectiation/期望。

    對(duì)于mock來(lái)說(shuō),exception是重中之重:我們期待方法有沒有被調(diào)用,期待適當(dāng)?shù)膮?shù),期待調(diào)用的次數(shù),甚至期待多個(gè)mock之間的調(diào)用順序。所有的一切期待都是事先準(zhǔn)備好,在測(cè)試過程中和測(cè)試結(jié)束后驗(yàn)證是否和預(yù)期的一致。

    而對(duì)于stub,通常都不會(huì)關(guān)注exception,就像上面給出的UserDaoStub的例子,沒有任何代碼來(lái)幫助判斷這個(gè)stub類是否被調(diào)用。雖然理論上某些stub實(shí)現(xiàn)也可以通過自己編碼的方式增加對(duì)expectiation的內(nèi)容,比如增加一個(gè)計(jì)數(shù)器,每次調(diào)用+1之類,但是實(shí)際上極少這樣做。 

    6. 總結(jié)

    關(guān)于mock和stub的不同,在Martin Fowler的"Mocks Aren't Stubs"一文中,有以下結(jié)束,我將它列出來(lái)作為總結(jié):

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

    (2) Fake 
    有實(shí)際可工作的實(shí)現(xiàn),但是通常有一些缺點(diǎn)導(dǎo)致不適合用于產(chǎn)品(基于內(nèi)存的數(shù)據(jù)庫(kù)就是一個(gè)好例子)。

    (3) Stubs 

    在測(cè)試過程中產(chǎn)生的調(diào)用提供預(yù)備好的應(yīng)答,通常不應(yīng)答計(jì)劃之外的任何事。stubs可能記錄關(guān)于調(diào)用的信息,比如 郵件網(wǎng)關(guān)的stub 會(huì)記錄它發(fā)送的消息,或者可能僅僅是發(fā)送了多少信息。

    (4) Mocks 

    如我們?cè)谶@里說(shuō)的那樣:預(yù)先計(jì)劃好的對(duì)象,帶有各種期待,他們組成了一個(gè)關(guān)于他們期待接受的調(diào)用的詳細(xì)說(shuō)明。

    3) 退化和轉(zhuǎn)化

    在實(shí)際的開發(fā)測(cè)試過程中,我們會(huì)發(fā)現(xiàn)其實(shí)mock和stub的界限有時(shí)候很模糊,并沒有嚴(yán)格的劃分方式,從而造成我們理解上的含糊和困惑。

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

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

    上面也談到過stub也是可以通過增加代碼來(lái)實(shí)現(xiàn)一些expectiation的特性,stub理論上也是可以向mock的方向做轉(zhuǎn)化,而從使得兩者的界限更加的模糊。

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

    評(píng)論

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

    非常不錯(cuò)!受教了!  回復(fù)  更多評(píng)論   

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

    寫的很棒,感激不盡,這個(gè)系列也很棒,希望博主繼續(xù)。  回復(fù)  更多評(píng)論   

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

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

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

    寫的清楚,支持下  回復(fù)  更多評(píng)論   

    主站蜘蛛池模板: 亚洲精品天堂成人片?V在线播放| 四虎成年永久免费网站| 欧洲乱码伦视频免费| 亚洲高清国产拍精品26U| 黄色三级三级三级免费看| 在线观看人成视频免费| 涩涩色中文综合亚洲| 久九九精品免费视频| 亚洲系列国产精品制服丝袜第| 久久香蕉国产线看免费| 亚洲av成人无码久久精品| 免费av一区二区三区| 亚洲欧洲无码AV电影在线观看| eeuss免费影院| 亚洲资源最新版在线观看| 午夜国产精品免费观看| 学生妹亚洲一区二区| 韩国欧洲一级毛片免费| 青青青亚洲精品国产| 又粗又大又长又爽免费视频| 香蕉视频免费在线| 亚洲性猛交xx乱| 4338×亚洲全国最大色成网站| 毛片基地看看成人免费| 国产亚洲人成网站在线观看不卡| 国产精品区免费视频| 4444亚洲国产成人精品| 色片在线免费观看| a毛片全部免费播放| 亚洲日韩乱码中文无码蜜桃| 成人网站免费观看| 日韩免费视频一区二区| 亚洲国产午夜精品理论片| 免费观看美女裸体网站| 无遮挡国产高潮视频免费观看| 亚洲欧洲无码AV电影在线观看| 8x网站免费入口在线观看| 免费无码又爽又刺激高潮视频| 亚洲综合色丁香麻豆| 日韩激情无码免费毛片| 黄视频在线观看免费|