單元測試常見問題
- 單元測試對接手人沒有意義
- 測試會間斷性的失敗
- ”測試“並沒有實際意義
- 測試需要過長的時間執行
- 測試沒有有效覆蓋代碼
- 測試與實現耦合太緊密,意味着一點點調整將會導致大量測試失敗
- 測試太復雜,需要預制太多條件
好的單元測試所要遵循的幾個原則
- [F]AST 快速性
- [I]solate 隔離性
- [R]epeatabel 可重復性
- [S]elf-Validating 自驗證性
- [T]imely 及時性
[F]IRST: Fast! 快速!
- 毫秒級的單元測試才是合格的單元測試
- 單元測試的首要原則是快速,只有以毫秒級別就能得到運行結果的單元測試才能使開發人員頻繁去運行它,而只運行修改部分的單元測試是不可行的(無法保證影響范圍),而將需求批量完成之后運行則會增加合入的難度。
- 單元測試的價值在於其可以針對系統提供持續的,有一定復雜度的並且是快速的反饋。
- 一種編寫快速單元測試的方案是重新組織代碼,將依賴於外部低速接口的邏輯單獨抽離出來,其他方法均依賴內存數據結構,這樣單元測試的編寫相對簡單,且對於依賴外部低速接口的方法,可以采用Mock的方式進行測試,保證接口的快速特性。
- 如果一個單元測試需要依賴數據庫,那這個單元測試是低效的
F[I]RST: Isolate your tests 隔離你的測試
- 隔離性指的兩個方面
- 不要依賴外部數據源等低速的,可能被共享的資源
- 單元測試彼此獨立,可單獨執行,不依賴測試套中單元測試的執行順序
- 每個單元測試僅關注邏輯的一個方面,有利於排錯
- 好的單元測試專注於要測試的一小塊代碼,也就是我們所說的“單元”,測試與越多代碼打交道,越容易偏離
- 不要依賴外部存儲,因其他人可能也正在使用,導致單元測試執行結果不可控
- 單元測試不要依賴於其他單元測試,即時是同一個測試套,即時仔細調整了順序,還是有可能因為依賴鏈的問題失敗,因此測試間也要彼此隔離。
- 在任何時間以任何順序執行任何一個測試都不會失敗
- 一個單元測試應該關注與邏輯的一個方面,如果要在某個單元測試中加入斷言,需要考慮一個,這個斷言真的是我需要的嗎?如果確實描述了某個場景,那是否要將該斷言抽取為一個測試方法呢?
- 測試用例的單一職責反過來會要求類/方法的單一職責(SRP),這也同時反向驅動了類/方法的完善
FI[R]ST: Good Tests Should Be [R]epeatable 理想的單元測試需要是可重復的
- 可重復測試意味着每次運行得到同樣的結果,也即不能依賴於任何外部不可控的因素
- 使用mock對象避免依賴不可控因素,如當前時間也是不可控因素,因其會發生變化
- 單元測試需要保持運行穩定,如果間歇性的失敗,會導致我們不斷的去查看這個測試,不可靠的測試也就失去了意義
FIR[S]T: [S]elf-Validating 自驗證
- 單元測試需要采用Asset語句等進行自驗證,也即當單元測試執行完畢之后就能得知測試情況,全程無需人工接入
- 人工接入效率低
- 人工接入不可靠
- 自驗證的測試用例可以引入持續集成(CI)提供質量保障
FIRS[T]: [T]imely 及時性
- 如果等代碼穩定運行再來補齊單元測試可能是低效的
- 最有效的方式是在代碼寫完之后立即進行單元測試,或者最好采用測試驅動開發模式
- 單元測試最好形成習慣,團隊內部如果能夠認識到單元測試的重要性,可以采用一些手段來保證,如
- 代碼檢查
- 未進行單元測試的代碼不允許提交
- 如果編寫單元測試成為習慣,那很大可能會倒逼代碼編寫時考慮易測試性,從而間接提高代碼質量
