大家都知道祭往,在軟件測(cè)試特別是在單元測(cè)試時(shí),必用的一個(gè)功能就是“斷言”(Assert)伦意,可能有些人覺得不就一個(gè)Assert語(yǔ)句,沒啥花頭硼补,也有很多人用起來(lái)也是懵懵懂懂驮肉,認(rèn)為只要是Assert開頭的方法,拿過(guò)來(lái)就用已骇。一個(gè)偶然的機(jī)會(huì)跟人聊到此功能离钝,覺得還是有必要在此整理一下如何使用以及對(duì)“斷言”的理解。希望可以幫助大家對(duì)此有一個(gè)系統(tǒng)的理解褪储,也趁機(jī)聊聊“斷言”發(fā)展一路過(guò)來(lái)的心路歷程卵渴。
基礎(chǔ)知識(shí)
首先稍微介紹一下斷言相關(guān)知識(shí),對(duì)于有經(jīng)驗(yàn)的程序員請(qǐng)移步到下面的“斷言”進(jìn)化史部分鲤竹。
什么是斷言
在單元測(cè)試時(shí)浪读,程序員預(yù)計(jì)在程序運(yùn)行到某個(gè)節(jié)點(diǎn)位置,需要判斷某些邏輯條件必須滿足辛藻,這樣下面的一些業(yè)務(wù)邏輯才可以進(jìn)行下去碘橘,如果不滿足,程序就會(huì)"報(bào)錯(cuò)"甚至是"崩潰"揩尸。比如說(shuō)蛹屿,一段程序是負(fù)責(zé)“轉(zhuǎn)賬”,在真正開始轉(zhuǎn)賬操作前首先需要“斷言”這個(gè)賬戶是一個(gè)“合法”的賬戶岩榆,比如賬戶不是null
错负。當(dāng)出現(xiàn)些狀況時(shí)坟瓢,程序開發(fā)人員就可以在第一時(shí)間知道這個(gè)問(wèn)題,可以去debug
除錯(cuò)犹撒,而非等到交付給用戶后才發(fā)現(xiàn)問(wèn)題折联。其實(shí)這個(gè)功能是TDD (Test Driven Develop)的基石之一。
“斷言” vs "異常"或者錯(cuò)誤识颊, 即 Assert vs. Exception/Error
- “斷言”通常是給程序開發(fā)人員自己使用诚镰,并且在開發(fā)測(cè)試期間使用。而異常等在程序運(yùn)行期間觸發(fā)
- 通诚榭睿“斷言”觸發(fā)后程序“崩潰”退出清笨,不需要從錯(cuò)誤中恢復(fù)。而“異橙絮耍”通常會(huì)使用try/catch等結(jié)構(gòu)從錯(cuò)誤中恢復(fù)并繼續(xù)運(yùn)行程序抠艾。
“斷言”進(jìn)化史
“石器時(shí)代”
一開始的一些單元測(cè)試框架(比如JUnit)提供的斷言語(yǔ)句,這樣在程序某個(gè)地方確保某個(gè)邏輯關(guān)系肯定返回是true,如果不是true,這個(gè)單元測(cè)試就是沒有測(cè)試通過(guò)桨昙。如下就是一個(gè)例子,如果程序運(yùn)行到此行時(shí)返回false程序就會(huì)拋出一個(gè)錯(cuò)誤(如下圖一)并停止運(yùn)行检号,開發(fā)人員可以去檢查下為什么出現(xiàn)此問(wèn)題。非常的簡(jiǎn)單粗爆蛙酪。
assert(x=y);
“青銅時(shí)代”
上面這種斷言除了簡(jiǎn)單之外齐苛,是有一個(gè)問(wèn)題,就是當(dāng)斷言被觸發(fā)時(shí)顯示出來(lái)的錯(cuò)誤消息不是很友好桂塞。如上圖一凹蜂,只是知道出錯(cuò)了,但是并沒有太多有用的信息藐俺,比如最好是能顯示出x與y的值來(lái)炊甲,這樣好更快的理解為啥出錯(cuò)。后來(lái)欲芹,支持?jǐn)嘌缘膯卧獪y(cè)試框架升級(jí)版本出現(xiàn)了卿啡,它們提供了一系列的高級(jí)”斷言“語(yǔ)句,添加了一些更加友好的程序接口菱父,同時(shí)還提供比較親民的錯(cuò)誤消息颈娜,比如下面的例子使用了兩個(gè)單獨(dú)的斷言語(yǔ)句。
int x=111;
int y=222;
assertEquals(x, y);
assertNotEquals(x, y);
執(zhí)行的結(jié)果如下圖二浙宜,你可以看到這個(gè)錯(cuò)誤結(jié)果相對(duì)于上面“石器時(shí)代”已經(jīng)包括了不少有用的信息官辽,比如除了知道斷言失敗外還顯示了期望的值
以及實(shí)際值
。
“黃金時(shí)代”
但是上面這種方式有一個(gè)弊端粟瞬,就是需要大量的預(yù)置斷言方法(比如判斷相等一個(gè)方法同仆,判斷不相等一個(gè)方法等),去支持各種場(chǎng)景裙品。接下來(lái)又出現(xiàn)了新的解決方案俗批,其中的明星就是Hamcrest
(其實(shí)這個(gè)詞是使用一種叫做angram的文字游戲俗或,即把一個(gè)原來(lái)單詞中的字母順序改變,這個(gè)Hamcrest就是從Matchers的變形)框架岁忘。是使用一種assertThat
組合上Matcher
來(lái)使用辛慰。
這個(gè)有多個(gè)好處,
- 首先是支持了在Java8中才遲遲引入的
流式編程(Stream)
干像,即每個(gè)Matcher執(zhí)行完后會(huì)再返回一個(gè)Matcher帅腌,這樣可以一個(gè)套一個(gè)組成一個(gè)Matcher鏈 - 另外Hamcrest還使用了非常接近于人類自然語(yǔ)言以及使用and/or/not等邏輯判斷的方式來(lái)寫測(cè)試方法,比如當(dāng)你看到下面的測(cè)試語(yǔ)句肯定會(huì)一目了然:
assertThat(actual, is(not(equalTo(expected)));
- 還有一個(gè)好處是輸出的斷言消息更加易讀麻汰。
- 另外還有一個(gè)好處即Hamcrest框架支持泛型
TypeSafe
速客,即在編譯時(shí)就會(huì)找到類型不匹配的錯(cuò)誤。比如下面第一個(gè)是傳統(tǒng)的斷言什乙,在編譯期不會(huì)報(bào)錯(cuò)挽封,但是運(yùn)行時(shí)會(huì)失敗,而第二個(gè)會(huì)在編譯時(shí)報(bào)錯(cuò)臣镣,就不用等到運(yùn)行期。
assertEquals("abc", 123); // 1
assertThat(123, is("abc")); // 2
- 使用Hamcrest的最后一個(gè)好處是對(duì)測(cè)試框架的“解耦合”智亮,即忆某,使用此框架你可以現(xiàn)在使用Junit后面可以轉(zhuǎn)到TestNG。甚至你自己去擴(kuò)展自己實(shí)現(xiàn)阔蛉。
總結(jié)
上面說(shuō)了這么多弃舒,是不是感覺平時(shí)經(jīng)常使用的一個(gè)看似簡(jiǎn)簡(jiǎn)單單的Assert還有不少的東西可以深挖一下滴。這個(gè)只是拋磚引玉状原,如果大家還有什么點(diǎn)子或建議請(qǐng)使用下面的方式聋呢。
聯(lián)系我:
- phray.zhang@gmail.com (email/郵件,whatsapp, linkedin)
- helloworld_2000 (wechat/微信)
- blog on github pages
- [簡(jiǎn)書 jianshu](http://www.reibang.com/users/a9e7b971aafc/latest_articles)
- github
- 微信公眾號(hào):vibex