置換測試:Stub,Mock

從測試的角度看拟逮,理想情況下撬统,我們的所作的全部測試都是對應(yīng)了實(shí)際的代碼,但這并不適用于實(shí)際情況敦迄,比如每次測試都去訪問數(shù)據(jù)庫恋追,或者都去加載許多和待測代碼毫無聯(lián)系的配置文件粒竖,這些不但會增加測試的時間開銷,同時也會增加測試用例的開發(fā)開銷几于。并且這樣也難以模擬一些特殊情況下的測試,比如需要特定的網(wǎng)絡(luò)接入條件沿后,或者當(dāng)天是某個特殊日期等等沿彭。

這時候我們可以用一些模擬的代碼來特?fù)Q實(shí)際代碼,從而幫助我們進(jìn)行測試尖滚。前兩天我司請來的老師來講的:Stub和Mock喉刘,就是兩種這樣的模擬代碼。

但是Stub和Mock的用法由于相似漆弄,都是為了替換外部依賴對象睦裳,從而經(jīng)常被理解混淆,或者根本沒有分清撼唾。但是實(shí)際上這是兩種完全不同的事物:

  1. 首先它們對于怎么去確定測試結(jié)果使用的方式是不同的廉邑,其中一個使用行為去確認(rèn)(behavior verification),一個使用狀態(tài)去確認(rèn)(state verification)
  2. 另一方面這是兩種將測試和設(shè)計結(jié)合在一起的方法倒谷,一個是自頂向下蛛蒙,一個是自下而上

簡言之,Stub更關(guān)注交互行為渤愁,為了驗(yàn)證待測系統(tǒng)調(diào)用目標(biāo)系統(tǒng)接口的交互行為牵祟,而Mock更關(guān)注狀態(tài),為了驗(yàn)證待測系統(tǒng)調(diào)用了目標(biāo)系統(tǒng)后抖格,目標(biāo)系統(tǒng)的狀態(tài)诺苹。

public class OrderStateTester {

    private WareHouse warehouse = new WareHouseImpl();
    
    @Before
    protected void setup() throws Exception {
        warehouse.setSize(50);
        warehouse.setLocation("Shang Hai");
    }

    @Test
    public void testOrderIsFilledIfEnoughInWarehouse() {
        Order order = new Order(50);
        order.fill(warehouse);
        assertTrue(order.isFilled());
        assertEquals(0, warehouse.getInventory());
    }

    @Test
    public void testOrderDoseNotRemoveIfNotEnough() {
        Order order = new Order(51);
        order.fill(warehouse);
        assertFalse(order.isFilled());
        assertEquals(50, warehouse.getInventory());
    }
}

類似這樣的TestCase是最常見的一種,可以看到我們需要測試的是Order對象雹拄。為了這個測試收奔,需要Order跟Warehouse,需要Warehouse的理由有兩個:首先需要通過它來配合測試,其次需要它來進(jìn)行驗(yàn)證(因?yàn)閛rder.fill改變了warehouse對象)
如果使用Mock對象會怎么樣呢办桨?有很多可用的mock對象庫筹淫,Mokito,JMock之類的呢撞,如果用jMock寫一段測試用例則會是:

  public void testFillingRemovesInventoryIfInStock() {
    Order order = new Order(50);
    Mock warehouseMock = new Mock(Warehouse.class);

    warehouseMock.expects(once()).method("hasInventory")
      .with(eq(content),eq(50))
      .will(returnValue(true));
    warehouseMock.expects(once()).method("remove")
      .with(eq(content), eq(50))
      .after("hasInventory");
    order.fill((Warehouse) warehouseMock.proxy());

    warehouseMock.verify();
    assertTrue(order.isFilled());
  }

可以看到在數(shù)據(jù)準(zhǔn)備的階段损姜,創(chuàng)建了一個Warehouse類的mock對象,接著設(shè)置了Mock的期望殊霞,這些期望也就是在測試order時會被執(zhí)行摧阅。
在驗(yàn)證階段,和之前一樣可以跑order對象的斷言绷蹲,其次可以調(diào)用mock的verify方法棒卷,驗(yàn)證它是否像期望的那樣去運(yùn)行顾孽。

關(guān)鍵不同點(diǎn)在于怎么樣去驗(yàn)證order在于warehouse的交互中做了正確的事。上面的testcase中比规,我通過warehouse的狀態(tài)去驗(yàn)證若厚。

如果對于Stub和Mock還是分不清楚的話,或許可以通過老師舉得MailSender的case來解釋一番:如果我們有一個發(fā)送郵件的服務(wù),會和待測對象交互:

public interface MailSender {
  public void send (Message msg);
}  

如果使用Stub去驗(yàn)證:

public class MailSenderStub implements MailSender {
  private List<Message> messages = new ArrayList<Message>();
  public void send (Message msg) {
    messages.add(msg);
  }
  public int numberSent() {
    return messages.size();
  }
}  
@Test
public void testOrder { 
       Order order = new Order(51); 
       MailSenderStub mailer = new MailSenderStub(); 
       order.setMailer(mailer); 
       assertEquals(1 , mailer.numberSent()); 
} 

我們不去關(guān)心它是否會發(fā)送給正確的人蜒什,或者發(fā)送的內(nèi)容是否正確测秸。
如果使用Mock去驗(yàn)證:

@Test
public void testOrderSendsMailIfUnFilled() { 
    Order order = new Order(51); 
    Mock mailer = mock(MailSender.class); 
    Mock warehouse = mock(Warehouse.class); 
    order.setMailer((MailSender)mailer.proxy()); 
    warehouse.expects(once()).method("hasInventory").withAnyArgument() 
    .will(returnValue(false)); 
    order.fill((Warehouse)warehouse.proxy()) 
} 

兩種方法都用了別的代碼替代真正的MailSender,不同的是Stub采用行為驗(yàn)證灾常,只要發(fā)送了郵件即可霎冯,而Mock采用了狀態(tài)驗(yàn)證。


QQ圖片20180719212107.jpg

在重新學(xué)習(xí)了Stub和Mock之后钞瀑,之前逐漸混淆的概念又有了新的理解沈撞,對于目前維護(hù)的系統(tǒng)中測試?yán)щy的問題,比如測試一段方法需要許多l(xiāng)ogger對象雕什,或者需要查詢DB缠俺,發(fā)現(xiàn)可以通過完善Stub來解決,而且由于目前系統(tǒng)的開發(fā)背景监徘,logger等無關(guān)對象(與代碼邏輯無關(guān))的實(shí)現(xiàn)在多個項(xiàng)目中都幾乎相同晋修,所以可以用一套統(tǒng)一的Stub來實(shí)現(xiàn)多個系統(tǒng)的測試。而對于那些我們關(guān)心它狀態(tài)的依賴凰盔,例如MessageSender墓卦,則可以通過Mock的方式實(shí)現(xiàn)并驗(yàn)證。

現(xiàn)在的老項(xiàng)目流傳下來的祖?zhèn)鱰est case幾乎沒有一個能跑通的户敬,下一步的目標(biāo)就是保證新代碼的測試覆蓋率落剪,以及在力所能及的范圍里面把老代碼的測試也搞起來 : )

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市尿庐,隨后出現(xiàn)的幾起案子忠怖,更是在濱河造成了極大的恐慌,老刑警劉巖抄瑟,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凡泣,死亡現(xiàn)場離奇詭異,居然都是意外死亡皮假,警方通過查閱死者的電腦和手機(jī)鞋拟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惹资,“玉大人贺纲,你說我怎么就攤上這事⊥什猓” “怎么了猴誊?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵潦刃,是天一觀的道長。 經(jīng)常有香客問我懈叹,道長乖杠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任澄成,我火速辦了婚禮滑黔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘环揽。我一直安慰自己,他們只是感情好庵佣,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布歉胶。 她就那樣靜靜地躺著,像睡著了一般巴粪。 火紅的嫁衣襯著肌膚如雪通今。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天肛根,我揣著相機(jī)與錄音辫塌,去河邊找鬼。 笑死派哲,一個胖子當(dāng)著我的面吹牛臼氨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播芭届,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼储矩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了褂乍?” 一聲冷哼從身側(cè)響起持隧,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逃片,沒想到半個月后屡拨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡褥实,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年呀狼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片性锭。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡赠潦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出草冈,到底是詐尸還是另有隱情她奥,我是刑警寧澤瓮增,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站哩俭,受9級特大地震影響绷跑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凡资,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一砸捏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧隙赁,春花似錦垦藏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至厚掷,卻和暖如春弟灼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冒黑。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工田绑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抡爹。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓掩驱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親冬竟。 傳聞我的和親對象是個殘疾皇子昙篙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)诱咏,斷路器苔可,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 單元測試實(shí)踐背景 測試環(huán)境定位bug時,需要測試同學(xué)協(xié)助手動發(fā)起相關(guān)業(yè)務(wù)URL請求袋狞,開發(fā)進(jìn)行遠(yuǎn)程調(diào)試問題:1焚辅、遠(yuǎn)程...
    Zeng_小洲閱讀 7,637評論 0 4
  • Martin Fowler的一篇文章。??Key point: two differences; SUT??'M...
    Luna_Lu閱讀 1,623評論 0 4
  • Mock 方法是單元測試中常見的一種技術(shù)苟鸯,它的主要作用是模擬一些在應(yīng)用中不容易構(gòu)造或者比較復(fù)雜的對象同蜻,從而把測試與...
    熊熊要更努力閱讀 28,335評論 2 25
  • 浩瀚的夜太空,阿媛穿著笨重的太空服在太空中飛行早处,她在著急的躲避著搜捕者的搜捕湾蔓,她想不明白自己為什么會在這里,而身后...
    木若語閱讀 219評論 0 1