<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Sung in Blog

               一些技術(shù)文章 & 一些生活雜碎
    一、引子



    JUnit源碼是我仔細(xì)閱讀過(guò)的第一個(gè)開(kāi)源項(xiàng)目源碼。閱讀高手寫的代碼能學(xué)到一些好的編程風(fēng)格和實(shí)現(xiàn)思路,這是提高自己編程水平行之有效的方法,因此早就想看看這些赫赫有名的框架是怎么回事了。今天就拿最簡(jiǎn)單的JUnit下手,也算開(kāi)始自己的源碼分析之路。

    JUnit作為最著名的單元測(cè)試框架,由兩位業(yè)界有名人士協(xié)力完成,已經(jīng)經(jīng)歷了多次版本升級(jí)(了解JUnit基礎(chǔ)、JUnit實(shí)踐)。JUnit總體來(lái)說(shuō)短小而精悍,有不少值得我們借鑒的經(jīng)驗(yàn)在里面;但是也有一些不足存在,當(dāng)然這對(duì)于任何程序來(lái)說(shuō)都是難免的。


    下面我們將從整體(宏觀)和細(xì)節(jié)(微觀)兩方面來(lái)分析JUnit源碼,以下分析基于3.8.1版。


    二、宏觀——架構(gòu)與模式



    打開(kāi)源碼文件,你會(huì)發(fā)現(xiàn)JUnit源碼被分配到6個(gè)包中:junit.awtui、junit.swingui、junit.textui、junit.extensions、junit.framework、junit.runner。其中前三個(gè)包中包含了JUnit運(yùn)行時(shí)的入口程序以及運(yùn)行結(jié)果顯示界面,它們對(duì)于JUnit使用者來(lái)說(shuō)基本是透明的。junit.runner包中包含了支持單元測(cè)試運(yùn)行的一些基礎(chǔ)類以及自己的類加載器,它對(duì)于JUnit使用者來(lái)說(shuō)是完全透明的。



    剩下的兩個(gè)包是和使用JUnit進(jìn)行單元測(cè)試緊密聯(lián)系在一起的。其中junit.framework包含有編寫一般JUnit單元測(cè)試類必須是用到的JUnit類;而junit.extensions則是對(duì)framework包在功能上的一些必要擴(kuò)展以及為更多的功能擴(kuò)展留下的接口。



    JUnit提倡單元測(cè)試的簡(jiǎn)單化和自動(dòng)化。這就要求JUnit的使用要簡(jiǎn)單化,而且要很容易的實(shí)現(xiàn)自動(dòng)化測(cè)試。整個(gè)JUnit的設(shè)計(jì)大概也是遵循這個(gè)前提吧。整個(gè)框架的骨干僅有三個(gè)類組成(下圖所示)。
    image

    ?????? 如果你掌握了TestCase、TestSuite、BaseTestRunner的工作方式,那么你就可以隨心所欲的編寫測(cè)試代碼了。



    ?????? 下面我們來(lái)看看junit.framework中類之間的關(guān)系,下圖是我根據(jù)源代碼分析出來(lái)的,大部分關(guān)系都表示了出來(lái)。

    image

    先來(lái)看看各個(gè)類的職責(zé)。Assert類提供了JUnit使用的一整套的斷言,這套斷言都被TestCase繼承下來(lái),Assert也就變成了透明的。Test接口是為了統(tǒng)一TestCase和TestSuite的類型;而TestCase里面提供了運(yùn)行單元測(cè)試類的方法;在TestSuite中則提供了加載單元測(cè)試類,檢驗(yàn)測(cè)試類格式等等的方法。TestResult故名思意就是提供存放測(cè)試結(jié)果的地方,但是在JUnit中它還帶有一點(diǎn)控制器的功能。


    在這里指出其中我認(rèn)為有些不妥的地方。圖上TestCase和TestResult之間是雙向的依賴關(guān)系,而在UML類圖的關(guān)系中指出:依賴關(guān)系總是單向的。就讓我們來(lái)看看這這個(gè)可疑的地方。


    TestCase中的代碼:

    /**

    * Runs the test case and collects the results in TestResult.
    */

    public void run(TestResult result) {
    //調(diào)用了result中的run方法,
    //TestResult按照名稱來(lái)看應(yīng)該是一個(gè)記錄測(cè)試結(jié)果的類,怎么還能run?
    ?????? result.run(this);

    }

    相應(yīng)得TestResult中的代碼:

    /**
    * Runs a TestCase.
    */
    protected void run(final TestCase test) {

    ?????? //開(kāi)始測(cè)試
    ?????? startTest(test);

    ?????? //這個(gè)匿名內(nèi)類的使用一會(huì)再講

    ?????? Protectable p= new Protectable() {
    ??????????????public void protect() throws Throwable {

    ????????????????????//天那,這里又調(diào)用了TestCase里面的runBare方法

    ????????????????????test.runBare();

    ???????????? }

    ??????};

    ?????? runProtected(test, p); //這個(gè)方法就是要執(zhí)行上面制定的匿名內(nèi)類
    ?????? endTest(test);



    }




    TestResult中runProtected方法:



    public void runProtected(final Test test, Protectable p) {



    ?????? try {



    ??????????????p.protect();



    ?????? }



    ?????? catch (AssertionFailedError e) {



    ??????????????addFailure(test, e);??????????????//給TestResult添加失敗記錄



    ?????? }



    ?????? catch (ThreadDeath e) { // don't catch ThreadDeath by accident



    ??????????????throw e;



    ?????? }



    ?????? catch (Throwable e) {



    ??????????????addError(test, e);????????//給TestResult添加出錯(cuò)記錄



    ?????? }



    }




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



    public void run(final TestResult result) {



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



    ?????? Protectable p= new Protectable() {



    ??????????????public void protect() throws Exception {



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



    setUp();



    ???????????????????? basicRun(result);



    ???????????????????? tearDown();



    ??????????????}



    ?????? };



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



    ?????? result.runProtected(this, p);



    }




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



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



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



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



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



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



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



    ?????? 組合模式:當(dāng)系統(tǒng)的測(cè)試用例慢慢變得多起來(lái),挨個(gè)運(yùn)行測(cè)試用例就成了一個(gè)棘手的問(wèn)題。作為一個(gè)方便使用的單元測(cè)試框架,這一點(diǎn)是必須解決的。因此JUnit里面提供了TestSuite的功能,它允許將多個(gè)測(cè)試用例放到一個(gè)TestSuite里面來(lái)一次執(zhí)行;而且要進(jìn)一步的支持TestSuite里面套TestSuite的功能。使用組合模式能夠很好的解決這個(gè)問(wèn)題。
    在上面我們已經(jīng)提到了junit.extentions包中的內(nèi)容TestSetup。來(lái)看看整個(gè)包的結(jié)構(gòu)吧。image


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



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

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


    image

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

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

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

    這樣,對(duì)JUnit的整體框架有了全面的認(rèn)識(shí)。總體來(lái)說(shuō)各個(gè)包分工明確,設(shè)計(jì)上采用了必要的設(shè)計(jì)模式來(lái)增強(qiáng)了擴(kuò)展性和重用性,很值得學(xué)習(xí)和借鑒。
    posted on 2005-10-16 13:06 Sung 閱讀(708) 評(píng)論(0)  編輯  收藏 所屬分類: Java
    主站蜘蛛池模板: 免费看国产一级片| 亚洲国产精品一区二区久久hs| MM1313亚洲国产精品| 国产亚洲成人久久| 99re这里有免费视频精品| 亚洲精品美女久久7777777| 在线日韩日本国产亚洲| 无码区日韩特区永久免费系列 | 日韩精品无码免费视频| 亚洲国产精品无码AAA片| 性xxxxx免费视频播放| 青青视频免费在线| 久久精品国产亚洲av麻豆小说| 成人毛片18女人毛片免费| 中文字幕无码毛片免费看| 亚洲人6666成人观看| 亚洲日本中文字幕天堂网| 国产人成免费视频网站| 一区二区三区免费视频网站 | 日韩大片在线永久免费观看网站 | 日本永久免费a∨在线视频| 亚洲天堂一区二区三区| 亚洲伊人成无码综合网| 成年人网站在线免费观看| 中文字幕不卡免费视频| 亚洲一区二区无码偷拍| 亚洲国产成人久久精品动漫| 国产免费午夜a无码v视频| 1000部免费啪啪十八未年禁止观看| 黄色大片免费网站| 亚洲精品国产精品国自产网站| 国产亚洲无线码一区二区| 免费看美女被靠到爽的视频| 3344永久在线观看视频免费首页| 国产免费AV片在线观看播放| 亚洲国产成人无码AV在线| 亚洲熟妇无码爱v在线观看| 久久久青草青青亚洲国产免观| 国产大片91精品免费观看男同 | 免费a级毛片无码a∨免费软件| 国产亚洲男人的天堂在线观看|