隨著Refactoring技術和XP軟件工程技術的廣泛推廣,單元測試的作用在軟件工程中變得越來越重要,而一個簡明易學、適用廣泛、高效穩定的單元測試框架則對成功的實施單元測試有著至關重要的作用。在java編程語句環境里,Junit Framework是一個已經被多數java程序員采用和實證的優秀的測試框架,但是多數沒有嘗試Junit Framework的程序員在學習如何Junit Framework來編寫適應自己開發項目的單元測試時,依然覺得有一定的難度,這可能是因為Junit隨框架代碼和實用工具附帶的用戶指南和文檔的著重點在于解釋單元測試框架的設計方法以及簡單的類使用說明,而對在特定的測試框架(Junit)下如何實施單元測試,如何在項目開發的過程中更新和維護已經存在的單元測試代碼沒有詳細的解釋。因此本文檔就兩個著重點對Junit所附帶的文檔進行進一步的補充和說明,使Junit能被更多的開發團隊采用,讓單元測試乃至Refactoring、XP技術更好在更多的開發團隊中推廣。
1. 單元測試的編寫原則
Junit附帶文檔所列舉的單元測試帶有一定的迷惑性,因為幾乎所有的示例單元都是針對某個對象的某個方法,似乎Junit的單元測試僅適用于類組織結構的靜態約束,從而使初學者懷疑Junit下的單元測試所能帶來的效果。因此我們需要重新定義如何確定有價值的單元測試以及如何編寫這些單元測試、維護這些單元測試,從而讓更多的程序員接受和熟悉Junit下的單元測試的編寫。
在Junit單元測試框架的設計時,作者一共設定了三個總體目標,第一個是簡化測試的編寫,這種簡化包括測試框架的學習和實際測試單元的編寫;第二個是使測試單元保持持久性;第三個則是可以利用既有的測試來編寫相關的測試。從這三個目標可以看出,單元測試框架的基本設計考慮依然是從我們現有的測試方式和方法出發,而只是使測試變得更加容易實施和擴展并保持持久性。因此編寫單元測試的原則可以從我們通常使用的測試方法借鑒和利用。
2. 如何確定單元測試
在我們通常的測試中,一個單元測試一般針對于特定對象的一個特定特性,譬如,假定我們編寫了一個針對特定數據庫訪問的連接池的類包實現,我們會建立以下的單元測試:
在連接池啟動后,是否根據定義的規則在池中建立了相應數量的數據庫連接
申請一個數據庫連接,是否根據定義的規則從池中直接獲得緩存連接的引用,還是建立新的連接
釋放一個數據庫連接后,連接是否根據定義的規則被池釋放或者緩存以便以后使用
后臺Housekeeping線程是否按照定義的規則釋放已經過期的連接申請
如果連接有時間期限,后臺Housekeeping線程是否定期釋放已經過期的緩存連接
這兒只列出了部分的可能測試,但是從這個列表我們可以看出單元測試的粒度。一個單元測試基本是以一個對象的明確特性為基礎,單元測試的過程應該限定在一個明確的線程范圍內。根據上面所述,一個單元測試的測試過程非常類似于一個Use Case的定義,但是單元測試的粒度一般來說比Use Case的定義要小,這點是容易理解的,因為Use Case是以單獨的事務單元為基礎的,而單元測試是以一組聚合性很強的對象的特定特征為基礎的,一般而言一個事務中會利用許多的系統特征來完成具體的軟件需求。
從上面的分析我們可以得出,測試單元應該以一個對象的內部狀態的轉換為基本編寫單元。一個軟件系統就和一輛設計好的汽車一樣,系統的狀態是由同一時刻時系統內部的各個分立的部件的狀態決定的,因此為了確定一個系統最終的行為符合我們起始的要求,我們首先需要保證系統內的各個部分的狀態會符合我們的設計要求,所以我們的測試單元的重點應該放在確定對象的狀態變換上。
然而需要注意的并不是所有的對象組特征都需要被編寫成獨立的測試單元,如何在對象組特征里篩選有價值的測試單元的原則在JUnitTest Infected: Programmers Love Writing Tests一文中得到了正確的描述,你應該在有可能引入錯誤的地方引入測試單元,通常這些地方存在于有特定邊界條件、復雜算法以及需求變動比較頻繁的代碼邏輯中。除了這些特性需要被編寫成獨立的測試單元外,還有一些邊界條件比較復雜的對象方法也應該被編寫成獨立的測試單元,這部分單元測試已經在Junit文檔中被較好的描述和解釋過了。
文章來源于領測軟件測試網 http://www.kjueaiud.com/