為什么要進行單測試.
1. 單元測試的目的
一個單元測試從整個系統中單獨檢驗產品程序代碼的『一個單元』并檢查其得到的 結果是否是預期的。要測試的『一個單元』其大小是依據一組連貫的功能的大小及介于一個類別及一個包(package)之間實際上的變化(varies)。 其目的是在整合程序代碼到系統的其余部分之前先測試以便找出程序代碼中的臭蟲(bugs)。Junit等支持在Java程序代碼中撰寫單元測試。
在整合之前于系統其它部分隔離起來抓蟲的理由是因為那是比較容易的找到臭蟲(亦即比較快且便宜)及比較容易修正問題(并顯示其解決方式是可行的)。
單 元測試對于在初始整合一小部分程序代碼以及整合其余的改變之前提供了一些利益。如果有人需要變動現有的程序代碼,事實上單元測試仍然可以讓他對于其最后的 程序代碼更有信心;即他的改變不會破壞任何東西。愈好的單元測試讓人愈有信心--理想上測試必須在加入的新功能前也隨之更新。
2. 誰來撰寫單元測試及何時撰寫單元測試
程序代碼測試可能是非常乏味的,尤其是測試別人的程序,而當你是一個程序設計師的時候尤甚。但程序設計師喜歡撰寫程序,因此為什么不讓程序設計師撰寫一些程序可以作為測試之用呢?
當 單元測試正確的實作可以幫助程序設計師變的更有生產力,而同時提升開發程序代碼的品質。有一點你必須了解的是單元測試應該是開發程序的一部份是很重要的; 而且程序代碼的設計必須是可以測試的。目前的趨勢是在撰寫程序代碼之前要先撰寫單元測試,并且把焦點放在Java類別的接口及行為上。
先寫測試,再寫代碼的好處:
從技術上強制設計師先考慮一個類的功能,也就是這個類提供給外部的接口,而不至于太早陷入它的細節。這是面向對象提倡的一種設計原則。
好 的測試其實就是一個好的文檔,這個類使用者往往可以通過查看這個類的測試代碼了解它的功能。特別的,如果你拿到別人的一個程序,對他寫測試是最好的了解這 個程序的功能的方法。 xp的原則是 make it simple,不是很推薦另外寫文檔,因為項目在開發過程中往往處于變動中,如果在早期寫文檔,以后代碼變動后還得同步文檔,多了一個工作,而且由于項目 時間緊往往文檔寫的不全或與代碼不一致,與其這樣,不如不寫。而如果在項目結束后再寫文檔,開發人員往往已經忘記當時寫代碼時的種種考慮,況且有下一個項 目的壓力,管理人員也不愿意再為舊的項目寫文檔。導致以后維護的問題
沒有人能保證需求不變動,以往項目往往對需求的變動大為頭疼,害怕這 個改動會帶來其它地方的錯誤。為此,除了設計好的結構以分割項目外(松耦合),但如果有了測試,并已經建立了一個好的測試框架,對于需求的變動,修改完代 碼后,只要重新運行測試代碼,如果測試通過,也就保證了修改的成功,如果測試中出現錯誤,也會馬上發現錯在哪里。修改相應的部分,再運行測試,直至測試完 全通過。
軟件公司里往往存在開發部門和測試部門之間的矛盾:由于開發和測試分為兩個部門,多了一層溝通的成本和時間,溝通往往會產生錯誤 的發生。而且極易形成一個怪圈:開發人員為了趕任務,寫了爛爛的代碼,就把它扔給測試人員,然后寫其它的任務,測試當然是失敗的,又把代碼拿回去重寫,測 試就成了一個很頭疼的問題。這種怪圈的根源是責任不清,根據 xp 中的規定:寫這個代碼的人必須為自己的代碼寫測試,而且只有測試通過,才算完成這個任務(這里的測試包括所有的測試,如果測試時發現由于你的程序導致別的 組的測試失敗,你有責任通知相關人員修改直至集成測試通過),這樣就可以避免這類問題的發生。
簡而言之,如果程序設計師要寫一段代碼:
先用 junit 寫測試,然后再寫代碼;
寫完代碼,運行測試,如果測試失敗,
修改代碼,運行測試,直到測試成功。
如果以后對程序進行修改,優化 ( refactoring ),只要再運行測試代碼。如果所有的測試都成功,則代碼修改完成。
3. 單元測試與Java Team開發的結合
Java下的team開發,一般采用cvs(版本控制) + ant(項目管理) + junit(單元測試、集成測試)的模式:
每天早上上班,每個開發人員從 cvs server 獲取一個整個項目的工作拷貝。
拿到自己的任務,先用 junit 寫今天的任務的測試代碼。
然后寫今天任務的代碼,運行測試(單元測試),直到測試通過。
任務完成在下班前一兩個小時,各個開發人員把任務提交到cvs server。
然后由主管對整個項目運行自動測試(集成測試),哪個測試出錯,就找相關人員修改,直到所有測試通過。下班。。。
4. 測試控制工具中要有甚么?
無 論誰來撰寫單元測試或何時撰寫單元測試,我們的焦點應該放在檢驗程序代碼;主要是在于產生錯誤的風險。如果設計文件包含被測試對象的使用情節;便可成為好 的測試來源。不管如何,這些情節寫得不是很明確;因為這些情節實際上是以設計觀點所寫的--因此適當的測試應該有對等的情節,換句話說,也就是測試設計應 該盡可能的包含用戶實際使用程序時可能產生的動作或者過程。
另一個測試案例好的來源是在整合后從產品程序代碼當中找到的問題,維修問題的處理方式往往值得封裝成為測試案例。
5. 為什么要使用Junit等工具呢?
前面的論述說明為什么我們需要測試控制工具,但為什么我們使用Junit這些工具呢?
首先,它們是完全Free的啦!。
第二點,使用方便。
l 在你提升程序代碼的品質時JUnit測試仍允許你更快速的撰寫程序
那 聽起來似乎不是很直覺,但那是事實。當你使用JUnit撰寫測試,你將花更少的時間除蟲,同時對你程序代碼的改變更 俱有信心。這個信心讓你更積極重整程序代碼并增加新的功能。沒有測試,對于重整及增加新功能你會變得沒有信心;因為你不知道有甚么東西會破壞產出的結果。 采用一個綜合的測試系列,你可以在改變程序代碼之后快速的執行多個測試并對于你的變動并未破壞任何東西感到有信心。在執行測試時如果發現臭蟲,原始碼仍然 清楚的在你腦中,因此很容易找到臭蟲。在JUnit中撰寫的測試幫助你以一種極 大(extreme)的步伐撰寫程序及快速的找出缺點。
l JUnit非常簡單
撰 寫測試應該很簡單--這是重點!如果撰寫測試太復雜或太耗時間,便無法要求程序設計師撰寫測試。使用JUnit你可以快速的撰寫測試并檢測你的程序代碼并 逐 步隨著程序代碼的成長增加測試。只要你寫了一些測試,你想要快速并頻繁的執行測試而不至于中斷建立設計及開發程序。使用JUnit執行測試就像編譯你的程 序代碼那么容易。事實上,你應該執行編譯時也執行測試。編譯是檢測程序代碼的語法而測試是檢查程序代碼的完整性(integrity)。
l JUnit測試檢驗其結果并提供立即的回饋。
如果你是以人工比對測試的期望與實際結果那么測試是很不好玩的,而且讓你的速度慢下來。JUnit測試可以自動執行并且檢查他們自己的結果。當你執行測試,你獲得簡單且立即的回饋; 比如測試是通過或失敗。而不再需要人工檢查測試結果的報告。
l JUnit測試可以合成一個測試系列的層級架構。
JUnit可以把測試組織成測試系列;這個測試系列可以包含其它的測試或測試系列。JUnit測試的合成行為允許你組合多個測試并自動的回歸(regression)從頭到尾測試整個測試系列。你也可以執行測試系列層級架構中任何一層的測試。
l 撰寫JUnit測試所費不多。
使 用Junit測試框架,你可以很便宜的撰寫測試并享受由測試框架所提供的信心。撰寫一個測試就像寫一個方法一樣簡單;測試是檢驗要測試的程序代碼并定義期 望的結果。這個測試框架提供自動執行測試的背景;這個背景并成為其它測試集合的一部份。在測試少量的投資將持續讓你從時間及品質中獲得回收。
l JUnit測試提升軟件的穩定性。
你寫的測試愈少;你的程序代碼變的愈不穩定。測試使得軟件穩定并逐步累積信心;因為任何變動不會造成漣漪效應而漫及整個軟件。測試可以形成軟件的完整結構的膠結。
l JUnit測試是開發者測試。
JUnit 測試是高度區域性(localized)測試;用以改善開發者的生產力及程序代碼品質。不像功能測試(function test)視系統為一個黑箱以確認軟件整體的工作性為主,單元測試是由內而外測試系統基礎的建構區塊。開發者撰寫并擁有JUnit測試。每當一個開發反復 (iteration)完成,這個測試便包裹成為交付軟件的一部份 提供一種溝通的方式,「這是我交付的軟件并且是通過測試的。」
l JUnit測試是以Java寫成的。
使用Java測試Java軟件形成一個介于測試及程序代碼間的無縫(seamless)邊界。在測試的控制下測試變成整個軟件的擴充同時程序代碼可以被重整。Java編譯器的單元測試靜態語法檢查可已幫助測試程序并且確認遵守軟件接口的約定。
一 段測試的程序代碼無法單獨的執行,它需要是執行環境的一部份。同時,它需要自動執行的單元測試--譬如在系統中周期性的執行所有的測試以證明沒有任何東西 被破壞。由于單元測試需要符合特定的準則:一個成功的測試不應該是人工檢查的(那可要到天荒地老了啊),一個未通過測試的失敗應可以產出文件以供診斷修 改。而Junit可以提供給我們這些便利.。這樣所有測試開發者所需撰寫的只是測試碼本身了。跟optimizeit、Jtest那些昂貴而又超級麻煩的 tool比較起來,其利昭然可見!
下面是如何在實際運用中使用單元測試.
http://www.51testing.com/html/9/271.html我用的eclipse 3.1.2,其中就包含了Junit,可以直接使用了.
給出一個簡單的測試實例
HelloWorld.java
public class HelloWorld {
public HelloWorld() {
super();
// TODO Auto-generated constructor stub
}
public String say()
{
return "Hello World!";
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
TestHelloWorld.java
import junit.framework.TestCase;
public class TestHelloWorld extends TestCase {
public TestHelloWorld(String name)
{
super(name);
}
public void testSay() {
HelloWorld hi = new HelloWorld();
assertEquals("Hello World!", hi.say());
}
public static void main(String[] args) {
junit.textui.TestRunner.run(TestHelloWorld.class);
}
}
單元測試的FAQ.
http://junit.sourceforge.net/doc/faq/faq.htm