開發軟體的時候為程式碼編寫單元測試是很好的。但實際上,光有測試還不夠,還要編寫好的測試,這同樣重要。
要做到這一點,考慮遵循一些固執的原則,對測試程式碼給予一些關愛:
1. 保持測試程式碼的緊湊和可讀性
要做到這一點,應該要進行毫不留情的重構,就像對生產程式碼應該做的那樣。否則讓測試程式碼隨著時間腐化,就是在測試裡面製造可怕的遺留程式碼。如果測試不能很容易重構,那麼生產程式碼也很難重構,從而導致生產系統的遺留程式碼。始終做一個勇敢的重構者。
2. 避免編寫重複累贅的斷言
舉個例子,測試程式碼使用正規表示式生成內容,而這個正規表示式是跟生產程式碼的解析器中使用的一模一樣的。
一般來說,我們不希望在測試和程式碼之間複製邏輯。因此,在測試中複製正規表示式或其他內容不是一種選擇。在這種情況下,考慮測試輸入激勵/輸出結果之間的關係(f(輸入) – >
輸出)可能會有幫助,例如,如果程式碼的目標是要做模板替換,不要在測試程式碼裡用原始值來做替換。相反,在測試裡面直接指定預期的計算結果。
// 使用Assertions.assertThat(processTemplate("param1", "param2")).isEqualTo("this is 'param1', and this is 'param2'"));
// 而不要用Assertions.assertThat(processTemplate("param1", "param2")).isEqualTo("this is '%s', and this is '%s'", param1, param2));
複製程式碼
3. 覆蓋儘可能多的範圍,包括正面情況,以及(甚至更重要的)出錯的程式碼路徑。
通常,要做到這一點,最好的辦法試採用測試驅動開發(Test Driven Development)。通過TDD,人們可以在設計時識別可能會出錯的部分。不要羞於為一段小程式碼編寫一個簡單的測試用例。你永遠不知道什麼時候,為什麼以及以什麼方式,你會要用到甚至修改這段程式碼。
可以研究一下如何檢查測試的有效性,類似PIT這樣的工具可以進行變更測試,值得研究一下。
4. 不要Mock你不擁有的型別!
這不是一個硬界限,但越過這條線很可能會產生反作用力!
TDD是關於設計的,也是關於測試的,兩者一樣重要,在模擬外部API時,測試不能用於驅動設計,API屬於第三方;這個第三方可以,並且實際上也經常會更改API的簽名和行為。
想象一下Mock第三方Lib的程式碼。在第三方庫的某次升級之後,它的邏輯可能會改變,但測試套件仍會執行得很好,因為它被Mock了。所以後來,你認為一切都很好,畢竟構建牆是綠色的,軟體部署上去,然後……嘣
如果你感覺需要Mock第三方庫,可能表明你當前的設計與第三方庫沒有足夠的分離。
另一個問題是第三方庫可能很複雜,需要大量的Mock才能正常工作。這導致過度指定的測試和複雜的測試輔助裝置,這本身就損害了緊湊和可讀的目標。或者由於模擬外部系統過於複雜,從而導致測試程式碼對生產程式碼的覆蓋不足。
取而代之的最常見的方法,是圍繞外部lib / 系統建立包裝器,儘管應該意識到抽象洩漏的風險,其中過多的低階API,概念或異常超出了包裝器的邊界。為了驗證與第三方庫的整合,編寫整合測試,並使它們儘可能緊湊和可讀。
其他人已經寫過Mock他們不擁有的型別時經歷過的痛苦:
davesquared.net/2011/04/don…
www.markhneedham.com/blog/2009/1…
blog.8thlight.com/eric-smith/…
stackoverflow.com/questions/1…
5. 不要Mock一切,這是一種反模式
如果一切都被Mock,我們真的在測試生產程式碼嗎?該不Mock的時候,不要猶豫!
不要Mock值物件
為什麼人們甚至想要這樣做?
因為例項化物件太痛苦了! =>
不是正當理由。
如果建立新的物件太難了,那麼程式碼可能需要一些嚴肅的重構。另一種方法是為您的值物件建立構建器 – 有一些工具,包括IDE外掛,Lombok和其他。還可以在測試類路徑中建立有意義的工廠方法。
abstract class CustomerCreations {
public static Customer customer_with_a_single_item_in_the_basket() {
// long init sequence
}
}複製程式碼
Mockito專注於物件之間的相互操作,這是物件導向程式設計的核心部分。
6. 讀《The Growing Object Oriented Software Guided by Tests》這本書
這本書是一本必讀書。這本書帶著讀者從頭開始構建一個完整功能的應用。作者解釋了測試驅動的開發的很多方面,以及在專案生命週期的不同階段,如何做到有效測試。
注:本文主要內容翻譯自Mockito的網站文章 github.com/mockito/moc…