從這個(gè)main方法入口,首先JUnit將要分析命令行的參數(shù),然后將檢查測(cè)試類是否包含符合標(biāo)準(zhǔn)的suite方法,如果有將執(zhí)行方法中的內(nèi)容(見圖中0、1部分);如果沒有找到將自動(dòng)生成一個(gè)TestSuite,這樣跳過了圖中的0部分,并將測(cè)試用例類作為參數(shù)傳入。
上面得到了一個(gè)TestSuite類型的對(duì)象,現(xiàn)在可以運(yùn)行測(cè)試了,不過在運(yùn)行前要先加載TestResult和TestListener的對(duì)象,用來監(jiān)聽和記錄測(cè)試結(jié)果信息。剩下的在圖中可以很容易的看懂了,你可以參照源碼瀏覽一遍。
這里提出我的一點(diǎn)疑問。注意到JUnit實(shí)踐中提示將測(cè)試類中每個(gè)測(cè)試方法公用的初始化步驟放到setup方法中。這似乎會(huì)給你一種錯(cuò)覺,那是你會(huì)認(rèn)為setup與tearDown中的語(yǔ)句對(duì)于一個(gè)測(cè)試類中的所有測(cè)試方法只會(huì)運(yùn)行一次。但是實(shí)際上JUnit在實(shí)現(xiàn)上卻出乎意料,setup對(duì)于測(cè)試類中的每個(gè)測(cè)試方法都回運(yùn)行一遍。意思是說,你把公用的初始化代碼放到setup方法中僅僅是在代碼結(jié)構(gòu)上實(shí)現(xiàn)了重用,而沒有起到任何優(yōu)化系統(tǒng)的作用。比如你在setUp中初始化數(shù)據(jù)庫(kù)連接,那么這個(gè)過程將被執(zhí)行不只一次,這可有點(diǎn)……。我們來看下代碼:
//theClass為得到的TestCase類,name為此類其中的一個(gè)方法
static public Test createTest(Class theClass, String name) {
Constructor constructor;
try {
constructor= getTestConstructor(theClass);
……
Object test;
try {
//以下內(nèi)容為獲得一個(gè)TestCase對(duì)象,并將方法名稱傳入這個(gè)對(duì)象
if (constructor.getParameterTypes().length == 0) {
test= constructor.newInstance(new Object[0]);
if (test instanceof TestCase)
((TestCase) test).setName(name);
} else {
test= constructor.newInstance(new Object[]{name});
}
……
//返回這個(gè)對(duì)象
return (Test) test;
}
再看下運(yùn)行處的代碼,下面的方法是運(yùn)行在setUp和tearDown中間的
protected void runTest() throws Throwable {
//fName是testcase對(duì)象所擁有的那個(gè)方法的名稱
assertNotNull(fName);
Method runMethod= null;
try {
//根據(jù)方法名由反射得到方法
runMethod= getClass().getMethod(fName, null);
}
……
//執(zhí)行測(cè)試方法
runMethod.invoke(this, new Class[0]);
……
}
這樣每執(zhí)行一個(gè)測(cè)試方法要運(yùn)行一遍setUp和tearDown方法,大概是這樣一個(gè)過程:
恩……,也許這是為了兼容老的版本,也許是……。還好,JUnit提供了一個(gè)補(bǔ)救的擴(kuò)展類,那是我們上面提到的TestSetup,在這里類里面真正的實(shí)現(xiàn)了setUp、tearDown方法的提取使用。你在使用的時(shí)候,通過繼承來實(shí)現(xiàn)自己的setUp、tearDown方法,并使用裝飾模式獨(dú)有的調(diào)用方式來使用它可以了。
在閱讀的過程中,代碼風(fēng)格上給我明顯的感覺是,代碼基本上全多做到了細(xì)化,將每個(gè)功能點(diǎn)單獨(dú)提取到一個(gè)方法中,這樣提高了代碼的可復(fù)用性?墒窃陂喿x的時(shí)候,在方法間頻繁的跳躍,實(shí)在不是件好事,如果沒有IDE的幫助,我非要暈掉不可。
因此我認(rèn)為在提高代碼重用上還是要堅(jiān)持這樣的一條原則:到必要的時(shí)候再下手。是說,在你剛開始寫代碼的時(shí)候不要考慮什么重用和擴(kuò)展,只有當(dāng)你真正需要復(fù)用某段代碼或者擴(kuò)展系統(tǒng)時(shí),在動(dòng)手吧(記得在某位牛人的書上是這么來比喻的:讓第一顆子彈打中你)。
JUnit中使用的是老版本java collection,這大概是因?yàn)镴Unit初版本出現(xiàn)的時(shí)候還沒有新版collection推出。這種代碼不應(yīng)該出現(xiàn)在我們現(xiàn)在編寫的代碼中了,請(qǐng)注意。
好了,基本上分析完了JUnit的代碼,不知道你學(xué)到了什么。希望本文能夠起到拋磚引玉的作用。