單元測試之道Java------使用JUnit------

 

1 序言

 

·什么是單元測試

單元測試是開發(fā)者編寫的一小段代碼,用于檢驗被測代碼的一個很小的、很明確的功能是否正確。通常而言,一個單元測試是用于判斷某個特定條件(或者場景)下某個特定函數(shù)的行為。

 

·為什么要使用單元測試

單元測試不但會使你的工作完成得更輕松,而且會令你的設(shè)計變得更好。甚至大大減少你花在調(diào)試上面的時間。

 

·不寫測試的借口

編寫單元測試太花時間了

運行測試的時間太長了

測試代碼并不是我的工作

我并不清楚代碼的行為,所以也就無從測試

但是這些代碼都能夠編譯通過

公司請我來試我是為了寫代碼,而不是寫測試

如果我讓測試員或者QA人員沒有工作,那么我會覺得很內(nèi)疚

我的公司并不會讓我在真實系統(tǒng)中運行單元測試

 

3 使用JUnit編寫測試

 

3.1 構(gòu)建單元測試

  測試代碼必須要做以下幾件事情:

·準(zhǔn)備測試所需要的各種條件(創(chuàng)建所有必須的對象,分配必要的資源等等)。

·調(diào)用要測試的方法。

·驗證被測試方法的行為和期望是否一致。

·完成后清理各種資源。

 

3.2 JUnit的各種斷言

  JUnit提供了一些輔助函數(shù),用于幫助你確定某個被測試函數(shù)是否工作正常。通常而言,我們把所有這些函數(shù)統(tǒng)稱為斷言。斷言是單元測試最基本的組成部分。

  當(dāng)一個失敗或錯誤出現(xiàn)的時候,當(dāng)前測試方法的執(zhí)行流程將會被種植,但是(位于同一個測試類中的)其他測試將會繼續(xù)運行。

 

 · assertEquals

assertEquals([String message],

expected,

actual)

 

這是使用得最多的斷言形式。在上面的參數(shù)中,expected是你的期望值(通常都是硬編碼的),actual是被測試代碼實際產(chǎn)生的值,message是一個可選的消息,如果提供的話,將會在發(fā)生錯誤的時候報告這個消息。當(dāng)然,你完全可以不提供這個message參數(shù),而只提供expectedvalue這兩個值。

任何對象都可以拿來做相等性測試:適當(dāng)?shù)南嗟刃耘袛喾椒〞挥脕碜鲞@樣的比較。值得注意的是使用原生數(shù)組的equals方法時,它并不是比較數(shù)組的內(nèi)容,而只是比較數(shù)組引用本身,而這大概不是你希望的吧。

計算機并不能精確地表示所有的浮點數(shù),通常都會有一些偏差。因而,如果你想用斷言來比較浮點數(shù)(在Java中,是類型為float或者double的數(shù)),則需要制定一個額外的誤差參數(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)

 

驗證一個給定的對象是否為null(或者為非null),如果答案為否,則將會失敗。message參數(shù)是可選的。

 

 

·assertSame

assertSame([String message], expected, actual)

 

驗證expected參數(shù)和actual參數(shù)所引用的是否為同一個對象,如果不是的話,將會失敗。message參數(shù)是可選的。

 

assertNotSame([String message], expected, actual)

 

驗證expected參數(shù)和actual參數(shù)所應(yīng)用的是否為不同的對象,如果是相同的話,將會失敗。message參數(shù)是可選的。

 

 

·assertTrue

assertTrue([String message], Boolean condition)

 

驗證給定的二元條件是否為真,如果為假的話,將會失敗。meesage參數(shù)是可選的。

如果你發(fā)現(xiàn)測試代碼像下面這樣,宛如廢話一般:

asserTrue(true);

那么你就該好好想想這些代碼了。對于這種寫法,除非是被用于確認(rèn)某個分支,或者一場邏輯才有可能是正確的選擇;否則的話,很可能就是一個糟糕的主意。

 

assertFalse([String message], Boolean condition)

上面的代碼用于驗證給定的二元條件是否為假。如果不是的話,該測試將會失敗。

 

 

·fail

fail([String message])

上面的斷言將會使測試立即失敗,其中message參數(shù)是可選的。這種斷言通常被用于標(biāo)記某個不應(yīng)該被到達(dá)的分支(譬如,在一個與預(yù)期發(fā)生的異常之后)。

 

 

·使用斷言

一般而言,一個測試方法會包含有多個斷言,因為你需要驗證該方法的多個方面以及內(nèi)在的多種聯(lián)系。

當(dāng)有測試失敗的時候,無論如何都不能給原有代碼再添加新的特性!此時你應(yīng)該盡快地修復(fù)這個錯誤,直到讓所有的測試都能順利通過。

 

 

3.3 JUnit框架

每個包含測試的類都必須如所示那樣由TestCase繼承而來。基類TestCase提供了我們所需的大部分單元測試功能,包括所有在前面講述過的斷言方法。

基類需要一個以String為參數(shù)的構(gòu)造函數(shù),因而我們必須調(diào)用super以傳遞這么一個名字。

測試類包含了名為test…的方法。而所有以test開頭的方法都會被JUnit自動運行。你還可以通過定義suite方法制定特殊的函數(shù)來運行。

 

 

3.4 JUnit測試的組成

一個測試類包含一些測試方法;每個方法包含一個或者多個斷言語句。但是測試類也能調(diào)用其它測試類:單獨的類、包、甚至完整的一個系統(tǒng)。可以通過創(chuàng)建test suite來取得。任何測試類都能包含一個名為suite的靜態(tài)方法。

public static Test suite();

你可以提供suite()方法來返回任何你想要的測試集合(沒有suite()方法,JUnit會自動運行所有的test…方法)。但是你可能需要手工添加特殊的測試,包括其他suite

 

 

· Per-methodSetupTear-down

JUnitTestCase基類提供兩個方法供你改寫,分別用于環(huán)境的建立和清理:

protected void setup();

protected void teardown();

 

 

· Per-suite SetupTear-down

一般而言,你只須針對每個方法設(shè)置運行環(huán)境;但是在某些情況下,你須為整個test suite設(shè)置一些環(huán)境,以及在test suite中的所有方法都執(zhí)行完成后做一些清理工作。要達(dá)到這種效果,你需要per-suite setupper-suite teardown

Per-suitesetup要復(fù)雜一些。你需要提供所需測試的一個suite(無論通過什么樣的方式)并且把它包裝進(jìn)一個TestSetup對象。

注意你可以在同一個類中同時使用per-sutieper-testsetup()teardown

 

 

3.5 自定義JUnit斷言

如果你有需要在整個項目中共享的斷言或者公共代碼,你也許需要考慮從TestCase繼承一個類并且使用這個字類來進(jìn)行所有的測試。

事實上,開始新項目時總是從自己的自定義基類繼承而不直接從JUnit的類繼承通常是一個好主意——即便你的基類在一開始沒有添加任何額外的功能。這樣做的好處是當(dāng)你需要添加一個所有測試類都需要的方法或者能力時,可以簡單地編輯你的基類而不需要改動項目中的所有test case

 

 

3.6 JUnit和異常

對于測試而言,下面兩種異常是我們可能會感興趣的:

1.  從測試代碼拋出的可預(yù)測異常。

2.  由于某個模塊(或代碼)發(fā)生嚴(yán)重錯誤,而拋出的不可預(yù)測異常。

 

任何對assertTrue(true)的使用都應(yīng)該被翻譯為“我預(yù)期控制流程會達(dá)到這個地方”。這對將來可能的誤解來說會起到強有力的文檔的作用。然而,不要忘記一個assertTrue(true)沒有被調(diào)用不會產(chǎn)生任何錯誤的。

通常而言,對于方法中每個被期望的異常,你都應(yīng)該寫一個專門的測試來確認(rèn)該方法在應(yīng)該拋出異常的時候確實會拋出異常。

對于處于出乎意料的異常,你最好簡單的改變你的測試方法的聲明讓它能拋出可能的異常。JUnit框架可以捕獲任何異常,并且把它報告為一個錯誤,這些都不需要你的參與。

 

 

3.7 關(guān)于命名的更多說明

如果編寫了一個測試,但是實現(xiàn)代碼還沒有準(zhǔn)備好,可以將以“test”打頭的測試方法名米功能為別的,譬如把“test”去掉,然后等準(zhǔn)備好了要來運行測試的時候再改回來。

無論如何,你要避免養(yǎng)成忽略“失敗的測試結(jié)果”的習(xí)慣。

 

 

3.8 JUnit測試骨架

JUnit寫測試真正所需要的就三件事:

1.  一個import語句引入所有junit.framework.*下的類。

2.  一個extends語句讓你的類從TestCase繼承。

3.  一個調(diào)用super(string)的構(gòu)造函數(shù)。