工作中,和同事對測試異常的最佳方法產生了分歧。
我是比較欣賞JUnit4的@Test(expected=FooException.class)的啦,覺得這樣多清爽啊,多declarative啊,再不用寫那麼一大坨try-fail-catch了。
不過同事(以下簡稱S)不這麼認爲。他覺得try-fail-catch挺好的,價格便宜,量又足,我們一直用它。而JUnit 4和TestNG提供的這個功能容易引誘程序員犯錯誤。
S給提出了一個挑戰:
[code]
public void testDoSomethingBad() {
initializeSomething();
try {
doSomethingBad();
fail();
} catch (FooException e) {}
}
[/code]
這裏面initializeSomething()的作用是初始化到某一個狀態,這個過程不應該出錯,而到了這個狀態之後,doSomethingBad()纔會拋異常。
然後他堅持認爲這種情況是最普遍的情況。而用annotation雖然看上去很美,但是可能邪惡地誘惑程序員寫出不準確的測試,造成false positive,比如,initializeSomething()拋了一個異常。
當然,我們對這種情況的常見程度各執己見。也沒什麼說的。但是,後來我想,其實,這個測試換成自然語言表達是什麼呢?大概是這樣吧?
[list]
[*] initializeSomething()不許出錯
[*] 在initializeSomething()之後doSomethingBad()要出錯
[/list]
那麼,爲什麼不把這兩個要求寫成兩個測試呢?
[code]
@Test
public void testInitializeSomething() {
initializeSomething();
}
@Test(expected=FooException.class)
public void testDoSomethingBadAfterInitializeSomething() {
initializeSomething();
doSomethingBad();
}
[/code]
只要我們寫測試的時候不要總想着“聰明”地實現,而是直白地用代碼表示需求,不就沒問題了麼?
再說一說我爲什麼這麼討厭這個try-fail-catch。它有幾個我深惡痛絕的毛病。
[list]
[*] 它等於代碼裏的邏輯分支。如果沒有拋異常,它執行fail(),而如果拋了異常,它進入catch()。而測試裏的邏輯分支味道很壞。它讓你的代碼容易出錯(比如,你忘了fail怎麼辦?測試一樣是綠的,但是你的bug還躲在那)。而且,它讓測試代碼不能達到100%的分支覆蓋率。本來如果用annotation的話,如果出現了initializeSomething()拋出異常的情況,覆蓋率馬上不是100%了,你可以很容易地發現問題。
[*] 冗長煩瑣。測試寫的不象spec,而象過程形代碼。
[*] 這個try-fail-catch只在你檢查Exception的時候成立。如果萬一你要檢查一個Error甚至是JUnit的AssertionFailedError,完了,你連fail()拋的異常也給截獲了。
[/list]
今天早晨,忽然靈機一動,其實,還有一個方法的。比如,在你自己的BaseTest的基類裏面,你可以實現一個expectException()的函數,然後這麼用:
[code]
public void testDoSomethingBad() {
initializeSomething();
expectException(FooException.class);
doSomethingBad();
}
[/code]
這樣,在runTest()結束前,可以檢查是否存在一個exception expectation,如果有,就catch住拋出來的異常,然後進行檢查。而如果沒出現異常,直接就報錯。這樣,不就沒問題了?還可以進一步抽象,弄個ExceptionExpectation的接口,這樣客戶代碼可以靈活地登記並且重用任何的異常期待,不僅僅侷限於檢查異常類型和錯誤信息了。
當然,這是在JUnit 3.8。
怎樣最有效地測試異常?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章
關於 Replace Temp With Query
這個I disagree系列裏面我準備把所有在工作中技術上的爭執記錄下來。也有立此存照的意思。也許再過幾年,回頭一看,會自己bs自己一把呢。今天要記錄
iteye_12278
2020-06-16 15:58:22
jdbc還是ibatis?
公司的一個大系統的持久層一直是直接使用jdbc。在jdbc的基礎上,又自制了一個簡陋的cache。每個持久功能的實現都比較類似,大致相當於這樣:[co
iteye_12278
2020-06-16 15:58:22
Web AOP?
iteye_12278
2020-02-24 11:01:01
這樣代碼重用?
iteye_12278
2020-02-24 11:01:01
依賴是否可以作爲一個獨立的衡量軟件質量的標準?
iteye_12278
2020-02-24 11:01:01
複雜還是不復雜?
iteye_12278
2020-02-24 11:01:01
一致性和Use Right Tool For Right Job哪個重要?
iteye_12278
2020-02-24 11:01:01
DRY與簡單性的平衡
iteye_12278
2020-02-24 11:01:01
getThis().getEvilAdvocate().setDead(getTrue())
iteye_12278
2018-08-31 06:51:00