1.1 名詞定義
單元測試框架 用於編寫、運行和查看單元測試及其結果的,容易編寫的,可重複使用的,並能覆蓋到測試代碼的所有重要部分的框架
狀態驗證 通過檢查被測試的系統與協作方在被測試方法執行後行爲的改變,判定被測試方法是否正確工作
1.2 第一個單元測試
這是一段需要被測試的代碼(在這裏需要注意對於類和方法的明明規則,也就是編程規則):
public class LogAnalyzer {
public boolean IsValidLogFileName(String fileName){
if (fileName.endsWith(".SLF")){ return false; }
return true;
}
}
單元測試的三個行爲:準備對象(Arrange),操作對象(Act),預測對象的某個行爲(Assert)。
所以按照這三個行爲編寫一個測試方法:
public void IsValidFileName_BadExTension_ReturnFalse(){
LogAnalyzer analyzer = new LogAnalyzer();
boolean result = analyzer.IsValidLogFileName("filewithbadextension.foo");
if (!result){
//do something;
}
}
這是一個錯誤檢驗,先準備好對象並進行操作,最後預測其返回的是false,然後do something,然後我們還要增加一個正檢驗,邏輯上同理。得到全部測試通過之後,需要做的就是減少重複的測試代碼進行代碼重構,保證其簡潔易懂。
1.3 重構
如果LogAnalyzer方法需要加入一個參數,之前的測試就需要全部修改了,這明顯不符合重複使用的原則。因此這裏需要加上可選添加的參數,使其能替換所需位置(原書中使用的是.NET語言,直接使用TestCase屬性即可)。
然後還需要保證對於不同的測試都要重新創建對象,不能讓舊的對象影響到之後的測試結果,所以測試代碼可以修改,創建對象的部分可以單獨拉出來,在一次測試完成之後需要把對象置空(這都是比較基本並且不考慮後果的做法,實際開發需要根據情況改變方式)。
當然,這種創建對象和對象置空的方式,都會極大地增加代碼量,使得測試代碼的可讀性變差,如果兩年後你已離職,其他程序員再來讀你的代碼,或許就會在心裏策馬奔騰了。所以這種策略需要仔細考量使用的位置和方式。比如書上介紹的使用工廠方式來創建對象。
1.4 檢驗異常
書中記錄檢驗異常的例子中有一個比較經典的例子說明。
public class LogAnalyzer {
public boolean IsValidLogFileName(String fileName){
try {
if (fileName.isEmpty()){
throw new Exception("filename has to be provided");
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
這段代碼就是在filename爲空的時候拋出異常,可是如果直接用上面的測試邏輯去檢測這個方法,會發現這一個方法都在一個大的try-catch裏面,如果構造函數有問題拋出異常,其實同樣也會使得測試通過。但構造函數絕不應該拋出異常。所以這個方法是不可取的。書中使用的是.NET的Assert.Catch 方法處理,在Java中應該有類似方法,之後再詳細進行相關學習
1.5 測試系統狀態改變
有時候測試的結果並不一定用返回值來檢驗,可能是系統狀態的改變或者某個顯示的變化,這些都是可以用來預測測試的結果的“工具”