JUnit原始碼分析(一)

2021-03-31 15:07:24 字數 4536 閱讀 9041

一、

引子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

中它還帶有一點控制器的功能。testlistener

介面抽象了所有測試監聽者的行為,他包括兩個新增錯誤和失敗的方法,開始測試和結束測試的方法。在

junit

框架中有兩個類實現了這個介面,乙個負責結果列印的

resultprinter

類,乙個是所有

testrunner

的基礎類

basetestrunner

類(這兩個類都不在

framework

包中)。

在這裡指出其中我認為有些不妥的地方。圖上

testcase

和testresult

之間是雙向的依賴關係,而在

uml類圖的關係中指出:依賴關係總是單向的。就讓我們來看看這這個可疑的地方。

testcase

中的**:

/*** runs the test case and collects the results in testresult.

*/public void run(testresult result)

相應得testresult

中的**:

/*** runs a testcase.

*/protected void run(final testcase test)

};runprotected(test, p);//

這個方法就是要執行上面制定的匿名內類

endtest(test);

}testresult

中runprotected

方法:public void runprotected(final test test, protectable p)

catch (assertionfailederror e)

catch (threaddeath e)

catch (throwable e)

}為什麼

junit

裡面會出現這樣奇怪的依賴關係,還有違反單一職責原則的

testresult

?當我看到

junit.extentions

包中的testsetup

時,也許我猜到了作者的用意。我們來看下

testsetup

中有關的**:

public void run(final testresult result)

};//

呼叫了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

的功能。使用組合模式能夠很好的解決這個問題。

模板模式:

junit

在testcase

這個抽象類中將整個測試的流程設定好了,比如先執行

setup

方法初始化測試前提,在執行測試方法,然後再

teardown

來取消測試設定。而這些步驟的具體實現都延遲到子類中去,也就是你實現的測試類中。

模板模式:

junit

在testcase

這個抽象類中將整個測試的流程設定好了,比如先執行

setup

方法初始化測試前提,在執行測試方法,然後再

teardown

來取消測試設定。而這些步驟的具體實現都延遲到子類中去,也就是你實現的測試類中。

JUnit原始碼分析

junit junit原始碼學習和經驗分享。源 裡沒有密碼,好多開源框架都出自大牛大師之手,是學習的好例子,可惜自己沒啥耐心去看,網上搜尋到牛人解析,記錄下來,學習學習。深入junit原始碼之runner 深入junit原始碼之statement 深入junit原始碼之rule 深入junit原始碼...

junit部分原始碼分析

通過對junit的粗略了解,大致的知道了這裡面的幾種模式 junit原始碼與之spring hibnate原始碼相比,就比較簡單了,但是麻雀雖小,五臟俱全,這裡面用到了幾 種設計模式,也是乙個短小精悍 非常完所的乙個框架。下面講乙個它的整體的框架吧 先得到testresult的物件,然後通過它的物件...

junit部分原始碼分析

通過對junit的粗略了解,大致的知道了這裡面的幾種模式 junit原始碼與之spring hibnate原始碼相比,就比較簡單了,但是麻雀雖小,五臟俱全,這裡面用到了幾 種設計模式,也是乙個短小精悍 非常完所的乙個框架。下面講乙個它的整體的框架吧 先得到testresult的物件,然後通過它的物件...