您的位置:軟件測試 > 開源軟件測試 > 開源單元測試工具 > junit
JUnit源碼分析
作者:網(wǎng)絡(luò)轉(zhuǎn)載 發(fā)布時(shí)間:[ 2013/1/18 13:59:32 ] 推薦標(biāo)簽:

為什么JUnit里面會(huì)出現(xiàn)這樣奇怪的依賴關(guān)系,還有違反單一職責(zé)原則的TestResult?當(dāng)我看到j(luò)unit.extentions包中的TestSetup時(shí),也許我猜到了作者的用意。我們來看下TestSetup中有關(guān)的代碼:

public void run(final TestResult result) {

       //又看到了上面類似的匿名內(nèi)部類

       Protectable p= new Protectable() {

              public void protect() throws Exception {

                     //不過這個(gè)內(nèi)部類里面的實(shí)現(xiàn)有所不同

      setUp();

                     basicRun(result);

                     tearDown();

              }

       };

       //調(diào)用了TestResult中的runProtected方法來執(zhí)行上面的實(shí)現(xiàn)

       result.runProtected(this, p);

}

這個(gè)類的產(chǎn)生是為了彌補(bǔ)TestCase類的一個(gè)小小的缺陷(具體請見下部分)。注意到在這個(gè)類里面也有和TestResult類似的匿名內(nèi)部類。這種匿名內(nèi)部類全是Protected接口的無名實(shí)現(xiàn),這里的目的我認(rèn)為有兩點(diǎn):

1)        由于內(nèi)部類可以在接下來的情景中完全不可見,而且不被任何人使用,因此也隱藏了接口的實(shí)現(xiàn)細(xì)節(jié)。

2)        為了提高可重用性,而使用內(nèi)部類比較快捷。這樣不管你protect方法里面具體執(zhí)行什么,對它錯(cuò)誤、失敗、異常捕捉的代碼(TestResult中的runProtected方法)可以重用了。

這也正是為什么會(huì)出現(xiàn)上面那樣奇怪的依賴關(guān)系:為了復(fù)用,要讓runProtected方法放在一個(gè)TestCase和TestSetup都能調(diào)用的地方。

不過我認(rèn)為為了復(fù)用而破壞了系統(tǒng)良好的結(jié)構(gòu)和可讀性,是需要仔細(xì)斟酌的。JUnit這樣的設(shè)計(jì)估計(jì)是為了以后框架多次擴(kuò)展后的重用考慮的。

說完了讓我費(fèi)解的問題。談?wù)勎矣X得JUnit框架中讓我感嘆的地方,那是小小的框架里面使用了很多設(shè)計(jì)模式在里面。而這些模式的使用也正是為了體現(xiàn)出整個(gè)框架結(jié)構(gòu)的簡潔、可擴(kuò)展。我將粗略的分析如下(模式應(yīng)用的詳細(xì)內(nèi)容請關(guān)注我關(guān)于設(shè)計(jì)模式的文章)。先看看在junit.framework里面使用的設(shè)計(jì)模式。

命令模式:作為輔助單元測試的框架,開發(fā)人員在使用它的時(shí)候,應(yīng)該僅僅關(guān)心測試用例的編寫,JUnit只是一個(gè)測試用例的執(zhí)行器和結(jié)果查看器,不應(yīng)該關(guān)心太多關(guān)于這個(gè)框架的細(xì)節(jié)。而對于JUnit來說,它并不需要知道請求TestCase的操作信息,僅把它當(dāng)作一種命令來執(zhí)行,然后把執(zhí)行測試結(jié)果發(fā)給開發(fā)人員。命令模式正是為了達(dá)到這種送耦合的目的。

組合模式:當(dāng)系統(tǒng)的測試用例慢慢變得多起來,挨個(gè)運(yùn)行測試用例成了一個(gè)棘手的問題。作為一個(gè)方便使用的單元測試框架,這一點(diǎn)是必須解決的。因此JUnit里面提供了TestSuite的功能,它允許將多個(gè)測試用例放到一個(gè)TestSuite里面來一次執(zhí)行;而且要進(jìn)一步的支持TestSuite里面套TestSuite的功能。使用組合模式能夠很好的解決這個(gè)問題。

在上面我們已經(jīng)提到了junit.extentions包中的內(nèi)容TestSetup。來看看整個(gè)包的結(jié)構(gòu)吧。

先簡要的介紹下包中各個(gè)類的功能。ActiveTestSuite對TestSuite進(jìn)行了改進(jìn),使得每個(gè)test運(yùn)行在一個(gè)單獨(dú)的線程里面,并且只到所有的線程都結(jié)束了才會(huì)結(jié)束整個(gè)測試。ExceptionTestCase是對TestCase進(jìn)行的改進(jìn),可以方便的判斷測試類是否拋出了期望的異常。而剩下的三個(gè)類,大概你看的出來是使用了裝飾模式來設(shè)計(jì)的。其中TestDecorator為具體裝飾類制定好了使用規(guī)則,RepeatedTest和TestSetup則是具體實(shí)現(xiàn)的裝飾類。

那為什么extentions包中ActiveTestSuite和ExceptionTestCase沒有使用裝飾模式呢?原因在于裝飾模式在結(jié)構(gòu)上要求存在類似于組合模式的遞歸。而對于已有的TestCase和TestSuite來說,直接繼承它們要比構(gòu)建一個(gè)新的遞歸結(jié)構(gòu)要來得快得多而且簡單;并且這些增強(qiáng)功能都只是針對TestCase或者TestSuite。使用了裝飾模式來擴(kuò)展的類與以上不同的是,它們功能的增強(qiáng)是針對任何Test實(shí)現(xiàn)的。如果不采用裝飾模式同樣的功能要為TestCase、TestSuite以及以后的其他Test實(shí)現(xiàn)分別寫出子類。因此使用裝飾模式能夠很巧妙的解決這個(gè)問題。

下面來介紹下junit.runner包。上面已經(jīng)提到,對于JUnit使用者來說,它可說是完全透明的,這個(gè)包里面提供了JUnit自己的測試類加載。下面是包中所有類的關(guān)系圖。

沒有什么好講的,都是使用反射機(jī)制來將測試類加載進(jìn)來,還有讀取properties文件的操作。如果想學(xué)習(xí)下反射機(jī)制的應(yīng)用可以閱讀這部分的源碼。

剩下的三個(gè)包這里也不作介紹,大部分的內(nèi)容都是GUI的繪制(當(dāng)然junit.textui包除外)。

JUnit中還使用了觀察者模式來完成單元測試結(jié)果的自動(dòng)更新(詳細(xì)內(nèi)容請見我關(guān)于觀察者模式的文章)。

這樣,對JUnit的整體框架有了全面的認(rèn)識(shí)?傮w來說各個(gè)包分工明確,設(shè)計(jì)上采用了必要的設(shè)計(jì)模式來增強(qiáng)了擴(kuò)展性和重用性,很值得學(xué)習(xí)和借鑒。

三、微觀——執(zhí)行流程與代碼風(fēng)格


來過一遍JUnit的執(zhí)行流程吧,這樣你能對JUnit有個(gè)清晰的認(rèn)識(shí),雖然作為一個(gè)使用者這完全是不必要的。從《JUnit in Action》直接拿來一張JUnit流程圖。

哦,也許你看暈了,我來當(dāng)下導(dǎo)游好了。上面已經(jīng)提到了TestRunner是BaseTestRunner的子類,在三個(gè)不同的ui包中各有一個(gè)TestRunner。這里我們僅以junit.textui包中的為例。

TestRunner作為入口程序是怎么被啟動(dòng)的呢?習(xí)慣了使用容器的我們現(xiàn)在也許很少考慮這個(gè)問題。那我們在TestRunner類里面找找吧,你看,你發(fā)現(xiàn)了這個(gè):

public static void main(String args[])

這不是我們寫小桌面程序時(shí)經(jīng)常打交道的main方法么?對,這么簡單。

上一頁123下一頁
軟件測試工具 | 聯(lián)系我們 | 投訴建議 | 誠聘英才 | 申請使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權(quán)所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd