單元測(cè)試之道Java版------使用JUnit------
第1章 序言
·什么是單元測(cè)試
單元測(cè)試是開發(fā)者編寫的一小段代碼,用于檢驗(yàn)被測(cè)代碼的一個(gè)很小的、很明確的功能是否正確。通常而言,一個(gè)單元測(cè)試是用于判斷某個(gè)特定條件(或者場(chǎng)景)下某個(gè)特定函數(shù)的行為。
·為什么要使用單元測(cè)試
單元測(cè)試不但會(huì)使你的工作完成得更輕松,而且會(huì)令你的設(shè)計(jì)變得更好。甚至大大減少你花在調(diào)試上面的時(shí)間。
·不寫測(cè)試的借口
編寫單元測(cè)試太花時(shí)間了
運(yùn)行測(cè)試的時(shí)間太長(zhǎng)了
測(cè)試代碼并不是我的工作
我并不清楚代碼的行為,所以也就無(wú)從測(cè)試
但是這些代碼都能夠編譯通過(guò)
公司請(qǐng)我來(lái)試我是為了寫代碼,而不是寫測(cè)試
如果我讓測(cè)試員或者QA人員沒有工作,那么我會(huì)覺得很內(nèi)疚
我的公司并不會(huì)讓我在真實(shí)系統(tǒng)中運(yùn)行單元測(cè)試
第3章 使用JUnit編寫測(cè)試
3.1 構(gòu)建單元測(cè)試
測(cè)試代碼必須要做以下幾件事情:
·準(zhǔn)備測(cè)試所需要的各種條件(創(chuàng)建所有必須的對(duì)象,分配必要的資源等等)。
·調(diào)用要測(cè)試的方法。
·驗(yàn)證被測(cè)試方法的行為和期望是否一致。
·完成后清理各種資源。
3.2 JUnit的各種斷言
JUnit提供了一些輔助函數(shù),用于幫助你確定某個(gè)被測(cè)試函數(shù)是否工作正常。通常而言,我們把所有這些函數(shù)統(tǒng)稱為斷言。斷言是單元測(cè)試最基本的組成部分。
當(dāng)一個(gè)失敗或錯(cuò)誤出現(xiàn)的時(shí)候,當(dāng)前測(cè)試方法的執(zhí)行流程將會(huì)被種植,但是(位于同一個(gè)測(cè)試類中的)其他測(cè)試將會(huì)繼續(xù)運(yùn)行。
· assertEquals
assertEquals([String
message],
expected,
actual)
這是使用得最多的斷言形式。在上面的參數(shù)中,expected是你的期望值(通常都是硬編碼的),actual是被測(cè)試代碼實(shí)際產(chǎn)生的值,message是一個(gè)可選的消息,如果提供的話,將會(huì)在發(fā)生錯(cuò)誤的時(shí)候報(bào)告這個(gè)消息。當(dāng)然,你完全可以不提供這個(gè)message參數(shù),而只提供expected和value這兩個(gè)值。
任何對(duì)象都可以拿來(lái)做相等性測(cè)試:適當(dāng)?shù)南嗟刃耘袛喾椒〞?huì)被用來(lái)做這樣的比較。值得注意的是使用原生數(shù)組的equals方法時(shí),它并不是比較數(shù)組的內(nèi)容,而只是比較數(shù)組引用本身,而這大概不是你希望的吧。
計(jì)算機(jī)并不能精確地表示所有的浮點(diǎn)數(shù),通常都會(huì)有一些偏差。因而,如果你想用斷言來(lái)比較浮點(diǎn)數(shù)(在Java中,是類型為float或者double的數(shù)),則需要制定一個(gè)額外的誤差參數(shù)。它表明你需要多接近才能認(rèn)為兩數(shù)“相等”。
assertEquals([String
message],
expected,
actual,
tolerance)
·assertNull
assertNull([String
message], java.lang.Object object)
assertNotNull([String
message], java.lang.Object object)
驗(yàn)證一個(gè)給定的對(duì)象是否為null(或者為非null),如果答案為否,則將會(huì)失敗。message參數(shù)是可選的。
·assertSame
assertSame([String
message], expected, actual)
驗(yàn)證expected參數(shù)和actual參數(shù)所引用的是否為同一個(gè)對(duì)象,如果不是的話,將會(huì)失敗。message參數(shù)是可選的。
assertNotSame([String
message], expected, actual)
驗(yàn)證expected參數(shù)和actual參數(shù)所應(yīng)用的是否為不同的對(duì)象,如果是相同的話,將會(huì)失敗。message參數(shù)是可選的。
·assertTrue
assertTrue([String
message], Boolean condition)
驗(yàn)證給定的二元條件是否為真,如果為假的話,將會(huì)失敗。meesage參數(shù)是可選的。
如果你發(fā)現(xiàn)測(cè)試代碼像下面這樣,宛如廢話一般:
asserTrue(true);
那么你就該好好想想這些代碼了。對(duì)于這種寫法,除非是被用于確認(rèn)某個(gè)分支,或者一場(chǎng)邏輯才有可能是正確的選擇;否則的話,很可能就是一個(gè)糟糕的主意。
assertFalse([String
message], Boolean condition)
上面的代碼用于驗(yàn)證給定的二元條件是否為假。如果不是的話,該測(cè)試將會(huì)失敗。
·fail
fail([String
message])
上面的斷言將會(huì)使測(cè)試立即失敗,其中message參數(shù)是可選的。這種斷言通常被用于標(biāo)記某個(gè)不應(yīng)該被到達(dá)的分支(譬如,在一個(gè)與預(yù)期發(fā)生的異常之后)。
·使用斷言
一般而言,一個(gè)測(cè)試方法會(huì)包含有多個(gè)斷言,因?yàn)槟阈枰?yàn)證該方法的多個(gè)方面以及內(nèi)在的多種聯(lián)系。
當(dāng)有測(cè)試失敗的時(shí)候,無(wú)論如何都不能給原有代碼再添加新的特性!此時(shí)你應(yīng)該盡快地修復(fù)這個(gè)錯(cuò)誤,直到讓所有的測(cè)試都能順利通過(guò)。
3.3 JUnit框架
每個(gè)包含測(cè)試的類都必須如所示那樣由TestCase繼承而來(lái)。基類TestCase提供了我們所需的大部分單元測(cè)試功能,包括所有在前面講述過(guò)的斷言方法。
基類需要一個(gè)以String為參數(shù)的構(gòu)造函數(shù),因而我們必須調(diào)用super以傳遞這么一個(gè)名字。
測(cè)試類包含了名為test…的方法。而所有以test開頭的方法都會(huì)被JUnit自動(dòng)運(yùn)行。你還可以通過(guò)定義suite方法制定特殊的函數(shù)來(lái)運(yùn)行。
3.4 JUnit測(cè)試的組成
一個(gè)測(cè)試類包含一些測(cè)試方法;每個(gè)方法包含一個(gè)或者多個(gè)斷言語(yǔ)句。但是測(cè)試類也能調(diào)用其它測(cè)試類:?jiǎn)为?dú)的類、包、甚至完整的一個(gè)系統(tǒng)。可以通過(guò)創(chuàng)建test suite來(lái)取得。任何測(cè)試類都能包含一個(gè)名為suite的靜態(tài)方法。
public static
Test suite();
你可以提供suite()方法來(lái)返回任何你想要的測(cè)試集合(沒有suite()方法,JUnit會(huì)自動(dòng)運(yùn)行所有的test…方法)。但是你可能需要手工添加特殊的測(cè)試,包括其他suite。
· Per-method的Setup和Tear-down
JUnit的TestCase基類提供兩個(gè)方法供你改寫,分別用于環(huán)境的建立和清理:
protected void setup();
protected void teardown();
· Per-suite
Setup和Tear-down
一般而言,你只須針對(duì)每個(gè)方法設(shè)置運(yùn)行環(huán)境;但是在某些情況下,你須為整個(gè)test suite設(shè)置一些環(huán)境,以及在test suite中的所有方法都執(zhí)行完成后做一些清理工作。要達(dá)到這種效果,你需要per-suite setup和per-suite teardown。
Per-suite的setup要復(fù)雜一些。你需要提供所需測(cè)試的一個(gè)suite(無(wú)論通過(guò)什么樣的方式)并且把它包裝進(jìn)一個(gè)TestSetup對(duì)象。
注意你可以在同一個(gè)類中同時(shí)使用per-sutie和per-test的setup()和teardown。
3.5 自定義JUnit斷言
如果你有需要在整個(gè)項(xiàng)目中共享的斷言或者公共代碼,你也許需要考慮從TestCase繼承一個(gè)類并且使用這個(gè)字類來(lái)進(jìn)行所有的測(cè)試。
事實(shí)上,開始新項(xiàng)目時(shí)總是從自己的自定義基類繼承而不直接從JUnit的類繼承通常是一個(gè)好主意——即便你的基類在一開始沒有添加任何額外的功能。這樣做的好處是當(dāng)你需要添加一個(gè)所有測(cè)試類都需要的方法或者能力時(shí),可以簡(jiǎn)單地編輯你的基類而不需要改動(dòng)項(xiàng)目中的所有test case。
3.6 JUnit和異常
對(duì)于測(cè)試而言,下面兩種異常是我們可能會(huì)感興趣的:
1. 從測(cè)試代碼拋出的可預(yù)測(cè)異常。
2. 由于某個(gè)模塊(或代碼)發(fā)生嚴(yán)重錯(cuò)誤,而拋出的不可預(yù)測(cè)異常。
任何對(duì)assertTrue(true)的使用都應(yīng)該被翻譯為“我預(yù)期控制流程會(huì)達(dá)到這個(gè)地方”。這對(duì)將來(lái)可能的誤解來(lái)說(shuō)會(huì)起到強(qiáng)有力的文檔的作用。然而,不要忘記一個(gè)assertTrue(true)沒有被調(diào)用不會(huì)產(chǎn)生任何錯(cuò)誤的。
通常而言,對(duì)于方法中每個(gè)被期望的異常,你都應(yīng)該寫一個(gè)專門的測(cè)試來(lái)確認(rèn)該方法在應(yīng)該拋出異常的時(shí)候確實(shí)會(huì)拋出異常。
對(duì)于處于出乎意料的異常,你最好簡(jiǎn)單的改變你的測(cè)試方法的聲明讓它能拋出可能的異常。JUnit框架可以捕獲任何異常,并且把它報(bào)告為一個(gè)錯(cuò)誤,這些都不需要你的參與。
3.7 關(guān)于命名的更多說(shuō)明
如果編寫了一個(gè)測(cè)試,但是實(shí)現(xiàn)代碼還沒有準(zhǔn)備好,可以將以“test”打頭的測(cè)試方法名米功能為別的,譬如把“test”去掉,然后等準(zhǔn)備好了要來(lái)運(yùn)行測(cè)試的時(shí)候再改回來(lái)。
無(wú)論如何,你要避免養(yǎng)成忽略“失敗的測(cè)試結(jié)果”的習(xí)慣。
3.8 JUnit測(cè)試骨架
用JUnit寫測(cè)試真正所需要的就三件事:
1. 一個(gè)import語(yǔ)句引入所有junit.framework.*下的類。
2. 一個(gè)extends語(yǔ)句讓你的類從TestCase繼承。
3. 一個(gè)調(diào)用super(string)的構(gòu)造函數(shù)。