二、JUnit簡(jiǎn)介及為什么要使用JUint
JUnit是對(duì)程序代碼進(jìn)行單元測(cè)試的一種Java框架。通過(guò)每次修改程序之后測(cè)試代碼,程序員可以保證代碼的的少量變動(dòng)不會(huì)破壞整個(gè)系統(tǒng)。要不是有Junit這樣的自動(dòng)化測(cè)試工具,代碼的的反復(fù)測(cè)試簡(jiǎn)直會(huì)把人累死而且還可能不準(zhǔn)確,F(xiàn)在好了,測(cè)試過(guò)程可以頻繁進(jìn)行而且還是自動(dòng)的,所以你可以令程序錯(cuò)誤降低到少。它寫(xiě)的是單元測(cè)試(Unit Test):軟件工程里的白盒測(cè)試,是測(cè)試某個(gè)類(lèi)的某個(gè)方法的功能。XP 中推崇的 test first design 是基于以上的技術(shù)。
如果你要寫(xiě)一段代碼:
1. 先用 junit 寫(xiě)測(cè)試,然后再寫(xiě)代碼
2. 寫(xiě)完代碼,運(yùn)行測(cè)試,測(cè)試失敗
3. 修改代碼,運(yùn)行測(cè)試,直到測(cè)試成功
如果以后對(duì)程序進(jìn)行修改,優(yōu)化 ( refactoring ),只要再運(yùn)行測(cè)試代碼,如果所有的測(cè)試都成功,則代碼修改完成。
Java 下的 team 開(kāi)發(fā),一般采用 cvs(版本控制) + ant(項(xiàng)目管理) + junit(集成測(cè)試) 的模式:
1. 每天早上上班,每個(gè)開(kāi)發(fā)人員從 cvs server 獲取一個(gè)整個(gè)項(xiàng)目的工作拷貝;
2. 拿到自己的任務(wù),先用 junit 寫(xiě)的任務(wù)的測(cè)試代碼;
3. 然后寫(xiě)任務(wù)的代碼,運(yùn)行測(cè)試,直到測(cè)試通過(guò),任務(wù)完成;
4. 在下班前一兩個(gè)小時(shí),各個(gè)開(kāi)發(fā)人員把任務(wù)提交到 cvs server;
5. 然后由主管對(duì)整個(gè)項(xiàng)目運(yùn)行自動(dòng)測(cè)試,哪個(gè)測(cè)試出錯(cuò),找相關(guān)人員修改,直到所有測(cè)試通過(guò)。下班...
先寫(xiě)測(cè)試,再寫(xiě)代碼的好處:
從技術(shù)上強(qiáng)制你先考慮一個(gè)類(lèi)的功能,也是這個(gè)類(lèi)提供給外部的接口,而不至于太早陷入它的細(xì)節(jié)。這是面向?qū)ο筇岢囊环N設(shè)計(jì)原則。好的測(cè)試其實(shí)是一個(gè)好的文檔,這個(gè)類(lèi)使用者往往可以通過(guò)查看這個(gè)類(lèi)的測(cè)試代碼了解它的功能。特別的,如果你拿到別人的一個(gè)程序,對(duì)他寫(xiě)測(cè)試是好的了解這個(gè)程序的功能的方法。 xp的原則是 make it simple,不是很推薦另外寫(xiě)文檔,因?yàn)轫?xiàng)目在開(kāi)發(fā)過(guò)程中往往處于變動(dòng)中,如果在早期寫(xiě)文檔,以后代碼變動(dòng)后還得同步文檔,多了一個(gè)工作,而且由于項(xiàng)目時(shí)間緊往往文檔寫(xiě)的不全或與代碼不一致,與其這樣,不如不寫(xiě)。而如果在項(xiàng)目結(jié)束后再寫(xiě)文檔,開(kāi)發(fā)人員往往已經(jīng)忘記當(dāng)時(shí)寫(xiě)代碼時(shí)的種種考慮,況且有下一個(gè)項(xiàng)目的壓力,管理人員也不愿意再為舊的項(xiàng)目寫(xiě)文檔,導(dǎo)致以后維護(hù)的問(wèn)題。沒(méi)有人能保證需求不變動(dòng),以往項(xiàng)目往往對(duì)需求的變動(dòng)大為頭疼,害怕這個(gè)改動(dòng)會(huì)帶來(lái)其他地方的錯(cuò)誤。為此,除了設(shè)計(jì)好的結(jié)構(gòu)以分割項(xiàng)目外(松耦合),但如果有了測(cè)試,并已經(jīng)建立了一個(gè)好的測(cè)試框架,對(duì)于需求的變動(dòng),修改完代碼后,只要重新運(yùn)行測(cè)試代碼,如果測(cè)試通過(guò),也保證了修改的成功,如果測(cè)試中出現(xiàn)錯(cuò)誤,也會(huì)馬上發(fā)現(xiàn)錯(cuò)在哪里,修改相應(yīng)的部分,再運(yùn)行測(cè)試,直至測(cè)試完全通過(guò)。
軟件公司里往往存在開(kāi)發(fā)部門(mén)和測(cè)試部門(mén)之間的矛盾:由于開(kāi)發(fā)和測(cè)試分為兩個(gè)部門(mén),多了一層溝通的成本和時(shí)間,溝通往往會(huì)產(chǎn)生錯(cuò)誤的發(fā)生。而且極易形成一個(gè)怪圈:開(kāi)發(fā)人員為了趕任務(wù),寫(xiě)了爛爛的代碼,把它扔給測(cè)試人員,然后寫(xiě)其他的任務(wù),測(cè)試當(dāng)然是失敗的,又把代碼拿回去重寫(xiě),而且在國(guó)內(nèi)往往一個(gè)軟件公司技術(shù)差的部門(mén)是測(cè)試部門(mén)(好的人都跑去寫(xiě)代碼了),測(cè)試成了一個(gè)很頭疼的問(wèn)題。這種怪圈的根源是責(zé)任不清,根據(jù) xp 中的規(guī)定:寫(xiě)這個(gè)代碼的人必須為自己的代碼寫(xiě)測(cè)試,而且只有測(cè)試通過(guò),才算完成這個(gè)任務(wù)(這里的測(cè)試包括所有的測(cè)試,如果測(cè)試時(shí)發(fā)現(xiàn)由于你的程序?qū)е聞e的模塊的測(cè)試失敗,你有責(zé)任通知相關(guān)人員修改直至集成測(cè)試通過(guò)),這樣可以避免這類(lèi)問(wèn)題的發(fā)生。
三、安裝
1. 獲取JUnit的軟件包,從Junit(http://www.junit.org/index.htm或http://download.sourceforge.net/junit/)下載新的軟件包。這里我使用的是http://download.sourceforge.net/junit/junit2.zip。
2. 將其在適當(dāng)?shù)哪夸浵陆獍ㄎ野惭b在D:junit2)。這樣在安裝目錄(也是你所選擇的解包的目錄)下你找到一個(gè)名為junit.jar的文件。將這個(gè)jar文件加入你的CLASSPATH系統(tǒng)變量。(IDE的設(shè)置會(huì)有所不同,參看你所喜愛(ài)的IDE的配置指南)JUnit安裝完了。
四、運(yùn)行
通過(guò)前面的介紹,我們對(duì)JUnit有了一個(gè)大概的輪廓。知道了它是干什么的。現(xiàn)在讓我們動(dòng)手改寫(xiě)上面的測(cè)試類(lèi)testCar使其符合Junit的規(guī)范--能在JUnit中運(yùn)行。
//執(zhí)行測(cè)試的類(lèi)(JUnit版)
import junit.framework.*;
public class testCar extends TestCase
{
protected int expectedWheels;
protected Car myCar;
public testCar(String name)
{
super(name);
}
protected void setUp()
{
expectedWheels = 4;
myCar = new Car();
}
public static Test suite()
{
/** the type safe way */
/*
TestSuite suite= new TestSuite();
suite.addTest(
new testCar("Car.getWheels")
{
protected void runTest()
{
testGetWheels();
}
}
);
return suite;
*/
/** the dynamic way */
return new TestSuite(testCar.class);
}
public void testGetWheels()
{
assertEquals(expectedWheels, myCar.getWheels());
}
}
改版后的testCar已經(jīng)面目全非。先讓我們了解這些改動(dòng)都是什么含義,再看如何執(zhí)行這個(gè)測(cè)試。
1>import語(yǔ)句,引入JUnit的類(lèi)。(沒(méi)問(wèn)題吧)
2>繼承 TestCase 。可以暫時(shí)將一個(gè)TestCase看作是對(duì)某個(gè)類(lèi)進(jìn)行測(cè)試的方法的集合。詳細(xì)介紹請(qǐng)參看JUnit資料
3>setUp()設(shè)定了進(jìn)行初始化的任務(wù)。我們以后會(huì)看到setUp會(huì)有特別的用處。
4>testGetWheeels()對(duì)預(yù)期的值和myCar.getWheels()返回的值進(jìn)行比較,并打印比較的結(jié)果。assertEquals是junit.framework.Assert中所定義的方法,junit.framework.TestCase繼承了junit.framework.Assert。
5>suite()是一個(gè)很特殊的靜態(tài)方法。JUnit的TestRunner會(huì)調(diào)用suite方法來(lái)確定有多少個(gè)測(cè)試可以執(zhí)行。上面的例子顯示了兩種方法:靜態(tài)的方法是構(gòu)造一個(gè)內(nèi)部類(lèi),并利用構(gòu)造函數(shù)給該測(cè)試命名(test name, 如 Car.getWheels),其覆蓋的runTest()方法,指明了該測(cè)試需要執(zhí)行那些方法--testGetWheels()。動(dòng)態(tài)的方法是利用內(nèi)省(reflection)來(lái)實(shí)現(xiàn)runTest(),找出需要執(zhí)行那些測(cè)試。此時(shí)測(cè)試的名字即是測(cè)試方法(test method,如testGetWheels)的名字。JUnit會(huì)自動(dòng)找出并調(diào)用該類(lèi)的測(cè)試方法。
6>將TestSuite看作是包裹測(cè)試的一個(gè)容器。如果將測(cè)試比作葉子節(jié)點(diǎn)的話(huà),TestSuite是分支節(jié)點(diǎn)。實(shí)際上TestCase,TestSuite以及TestSuite組成了一個(gè)composite Pattern。JUnit的文檔中有一篇專(zhuān)門(mén)講解如何使用Pattern構(gòu)造Junit框架。有興趣的朋友可以查看JUnit資料。