JUnir源碼分析(一)

來源:互聯網
上載者:User

一、引子



JUnit源碼是我仔細閱讀過的第一個開源項目源碼。閱讀高手寫的代碼能學到一些好的編程風格和實現思路,這是提高自己編程水平行之有效方法,因此早就想看看這些赫赫有名的架構是怎麼回事了。今天就拿最簡單的JUnit下手,也算開始自己的源碼分析之路。




 


JUnit作為最著名的單元測試架構,由兩位業界有名人士協力完成,已經經曆了多次版本升級(瞭解JUnit基礎、JUnit實踐)。JUnit總體來說短小而精悍,有不少值得我們借鑒的經驗在裡面;但是也有一些不足存在,當然這對於任何程式來說都是難免的。



下面我們將從整體(宏觀)和細節(微觀)兩方面來分析JUnit源碼,以下分析基於3.8.1版。




 


二、宏觀——架構與模式



開啟源碼檔案,你會發現JUnit源碼被分配到6個包中:junit.awtui、junit.swingui、junit.textui、junit.extensions、junit.framework、junit.runner。其中前三個包中包含了JUnit運行時的入口程式以及運行結果顯示介面,它們對於JUnit使用者來說基本是透明的。junit.runner包中包含了支援單元測試啟動並執行一些基礎類以及自己的類載入器,它對於JUnit使用者來說是完全透明的。



剩下的兩個包是和使用JUnit進行單元測試緊密聯絡在一起的。其中junit.framework包含有編寫一般JUnit單元測試類必須是用到的JUnit類;而junit.extensions則是對framework包在功能上的一些必要擴充以及為更多的功能擴充留下的介面。



JUnit提倡單元測試的簡單化和自動化。這就要求JUnit的使用要簡單化,而且要很容易的實現自動化測試。整個JUnit的設計大概也是遵循這個前提吧。整個架構的骨幹僅有三個類組成(下圖所示)。











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



       下面我們來看看junit.framework中類之間的關係,下圖是我根據原始碼分析出來的,大部分關係都表示了出來。








 


先來看看各個類的職責。Assert類提供了JUnit使用的一整套的斷言,這套斷言都被TestCase繼承下來,Assert也就變成了透明的。Test介面是為了統一TestCase和TestSuite的類型;而TestCase裡面提供了運行單元測試類的方法;在TestSuite中則提供了載入單元測試類,實驗室檢驗類格式等等的方法。TestResult故名思意就是提供存放測試結果的地方,但是在JUnit中它還帶有一點控制器的功能。



在這裡指出其中我認為有些不妥的地方。圖上TestCase和TestResult之間是雙向的依賴關係,而在UML類圖的關係中指出:依賴關係總是單向的。就讓我們來看看這這個可疑的地方。



TestCase中的代碼:



/**



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



*/



public void run(TestResult result) {



//調用了result中的run方法,



//TestResult按照名稱來看應該是一個記錄測試結果的類,怎麼還能run?



       result.run(this);



}



相應得TestResult中的代碼:



/**



* Runs a TestCase.



*/



protected void run(final TestCase test) {



       //開始測試



       startTest(test);



       //這個匿名內類的使用一會再講



       Protectable p= new Protectable() {



              public void protect() throws Throwable {



                     //天那,這裡又調用了TestCase裡面的runBare方法



                     test.runBare();



              }



       };



       runProtected(test, p); //這個方法就是要執行上面制定的匿名內類



       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添加出錯記錄



       }



}



為什麼JUnit裡面會出現這樣奇怪的依賴關係,還有違反單一職責原則的TestResult?當我看到junit.extentions包中的TestSetup時,也許我猜到了作者的用意。我們來看下TestSetup中有關的代碼:



public void run(final TestResult result) {



       //又看到了上面類似的匿名內部類



       Protectable p= new Protectable() {



              public void protect() throws Exception {



                     //不過這個內部類裡面的實現有所不同



setUp();



                     basicRun(result);



                     tearDown();



              }



       };



       //調用了TestResult中的runProtected方法來執行上面的實現



       result.runProtected(this, p);



}



這個類的產生是為了彌補TestCase類的一個小小的缺陷(具體請見下部分)。注意到在這個類裡面也有和TestResult類似的匿名內部類。這種匿名內部類全是Protected介面的無名實現,這裡的目的我認為有兩點:



1)        由於內部類可以在接下來的情景中完全不可見,而且不被任何人使用,因此也就隱藏了介面的實現細節。



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



這也正是為什麼會出現上面那樣奇怪的依賴關係:為了複用,就要讓runProtected方法放在一個TestCase和TestSetup都能調用的地方。



不過我認為為了複用而破壞了系統良好的結構和可讀性,是需要仔細斟酌的。JUnit這樣的設計估計是為了以後架構多次擴充後的重用考慮的。



說完了讓我費解的問題。談談我覺得JUnit架構中最讓我感歎的地方,那就是小小的架構裡面使用了很多設計模式在裡面。而這些模式的使用也正是為了體現出整個架構結構的簡潔、可擴充。我將粗略的分析如下(模式應用的詳細內容請關注我關於設計模式的文章)。先看看在junit.framework裡面使用的設計模式。



       命令模式:作為輔助單元測試的架構,開發人員在使用它的時候,應該僅僅關心測試案例的編寫,JUnit只是一個測試案例的執行器和結果查看器,不應該關心太多關於這個架構的細節。而對於JUnit來說,它並不需要知道請求TestCase的操作資訊,僅把它當作一種命令來執行,然後把執行測試結果發給開發人員。命令模式正是為了達到這種送耦合的目的。



       組合模式:當系統的測試案例慢慢變得多起來,挨個運行測試案例就成了一個棘手的問題。作為一個方便使用的單元測試架構,這一點是必須解決的。因此JUnit裡面提供了TestSuite的功能,它允許將多個測試案例放到一個TestSuite裡面來一次執行;而且要進一步的支援TestSuite裡面套TestSuite的功能。使用組合模式能夠很好的解決這個問題。




相關文章

Cloud Intelligence Leading the Digital Future

Alibaba Cloud ACtivate Online Conference, Nov. 20th & 21st, 2019 (UTC+08)

Register Now >

Starter Package

SSD Cloud server and data transfer for only $2.50 a month

Get Started >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。