以上是類圖結構,可以先瀏覽一下有個直觀的印象。
1. 功能設計
要實現的功能很簡單,如下
第一,我寫我的測試代碼,framework執行我的測試代碼,同時framework應該提供回到讓我去準備環境和銷毀環境。
第二,在我的測試的代碼中,可以檢查運行結果,對檢查的結果(測試成功/測試失敗)framework需要記錄。
2. 類設計
2.1 Assert類,在此類中包括各種斷言操作。測序代碼當檢查運行結果時,只需要調用這些方法,他會自動記錄檢查結果。
2.1 TestCase類,使用者寫測試代碼時繼承的基類. 為了方便將TestCase設計成可以執行多個測試代碼(也叫測試案例,英文TestCase,後面統一叫測試案例),而不是只用於一個測試案例。所以在預設情況下TestCase子類中所有public void testXxxx()風格的名字作為一個測試案例對待。在執行一個測試案例是調用setUp()方法去初始化環境,當一個測試案例執行完成後調用tearDown()方法銷毀測試環境。
framework會自動解析TestCase子類中方面簽名為public void testXxxx()風格的測試案例,並建立每個測試案例相應的TestCase對象。如果不想framework來完成這個工作你可以添加static suite方法,自己完成這個過程。
然後framework會對每個TestCase對象依次調用setUp()->public void
testXxxx()->tearDown()。如果你想改變對setUp()->public void
testXxxx()->tearDown()的調用(runTest方法預設邏輯)可以覆寫runTest方法,自己執行測試代碼。
為了檢查結果方便,TestCase繼承了Assert類,雖然他們不應該不是is a關係
2.3 TestSuite類,架構的一部分用例存放多個TestCase對象。上面討論的”自動解析TestCase子類中方面簽名為public void testXxxx()風格的測試案例,並建立每個測試案例相應的TestCase對象“就是由該類的構造方法完成(將TestCase子類名作為參數)
2.4 TestResult 用來存在Assert類斷言操作的結果。
2.5 TestRunner類。運行測試案例的起點,他的描述類類圖。
2.6 Test介面。將TestSuite和TestCase統一到Test介面,出於以下考慮
其一,出於可擴充性和靈活性考慮,在將來TestSuite中也可以放TestSuite類型的對象,這樣需要統一的介面來操作這個兩個類。
其二,還是出於可擴充性和靈活性考慮,給TestRunner一個統一才操作介面,雖然在BaseTestRunner中getTest方法中返回的Test都是TestSuitle。這樣TestRunner直接運行TestCase就很容易實現。
3. 參數收集模式的使用
3.1 參數收集模式
在此模式中,重複的將一個collection (例如list, map.)作為參數傳遞給一些方法,這些方法向此collection中新增內容。
範例代碼:
String[] userFiles = ...
List userList = new ArrayList();
for ( String userFile : userFiles ) {
addUsersTo(userFile , userList);
}
public void addUsersTo(String userFileName, List userList) {
...
}
3.2 JUnit3.8中此模式的使用
在JUnit3.8中, 此模式中的collection被封裝成TestResult類。在執行測試案例的過程中,framework建立一個TestResult對象,
然後用他作為參數去調用Test的run(TestResult)方法。值得注意的是,在TestCase類中,run(TestResult)方法又調用TestResult的
run(TestCase)方法。
為什麼要這麼處理呢?如果不這樣那麼,需要在下面兩個情況之中選擇。
1. 在TestCase代碼中需要捕捉斷言操作的結果,然後手動添加到TestResult中,例如
public TestCaseA extends TestCase{
...................
public void testXxxx(){
try {
........
assertTrue(...);
........
} catch (AssertionFailedError e) {
testResult.addFailure(test, e);
} catch (Throwable e) {
testResult.addError(test, e);
}
........................
}
這個情況下對TestCase之類的編寫者很不友好。而且為了在testXxx方法內部訪問當前TestResult變數也需要作出相應的處理。
也許作為testXxx方法的參數傳遞進來,也許作為TestCase的property。
2. 將“捕捉斷言操作的結果,然後手動添加到TestResult”的邏輯即
try {
........
} catch (AssertionFailedError e) {
testResult.addFailure(test, e);
} catch (Throwable e) {
testResult.addError(test, e);
}
放入斷言方法(例如assertTrue)內部。
這樣需要調整所有的assert方法,同樣的也要採取相應的方式讓assert方法能夠訪問當前TestResult變數。
上面兩個情況都不是很優雅,我們需要在一個地方統一的捕捉斷言操作的結果,然後添加到TestResult中,有了這個地方
還可以做一些類似的事情(例如TestCase執行觸發事件)而不至於讓邏輯侵入TestCase內部。JUnit3.8將這個地方選擇在TestResult
內部,即TestResult的run(TestCase)方法。在這個地方,統一的捕捉斷言操作的結果,然後添加到自己內部。
這樣也很優雅的就解決了TestResult變數訪問問題。在此方法內部在TestCase執行前後都會觸發相應事件。