06 August 2005

要寫多少 Unit Test 才夠?答案是沒有上限的,因為理論上可以達到無窮多個,而且隨著經驗/個性的不同,每個人的 Test 數量也不同。個人經手了兩個 Unit Test 專案的,有一些心得跟大家分享:

  • 最少要有一個 happy path 一片段的程式通常會有多個分歧 (if),最少 happy path 的 test 要先寫完再說。
  • 測到心裏不會發毛為止 -- 換句話說,一直測到你對片段的程式有絕對把握時,就可以不用再寫 Test。這個完全因人而異,有經驗的人對 API 很熟、或是對於某些問題都已經有 "套招" 可以解決,所以這種人寫到後來,Test 會變得較精簡,直接切中要害。雖然 Test 未必有 cover 到所有的排列組合,但是對有經驗的人來說,他有十足的把握,不會怕怕的... 心裏發毛...。那沒經驗的人呢?請保持戰戰兢兢的心情來寫程式,一步一步測,測多一點無所謂,直到 "戰戰兢兢" 的情緒沒有了,取而代之的是成就感,這才停手。當然沒經驗的人大多會測不到重點... 會漏東漏西的... 這個就沒有捷徑了,功力要慢慢累績才會漸漸提升。多跟 資深的人 pair 或是多多看 Effective Java 都可加快學習速度。
  • 從 debug 中學習 -- 在 Debug 的思路過程中,你會試著去想,有可能是幾個可疑之處造成的。而這些可疑之處就是要增加 Test 地方;除了 bug 的禍首之外,其他的可疑處也都要加寫 test。因為你對那些程式碼沒把握,才會對它疑神疑鬼的,造成你 "心裏發毛" ,增加心理負擔,所以通通都得加上 Test ,除之後快,讓心情舒暢。
  • 測試是一體 -- 通常 class 的每個 method 都有一個相對應的 testXXX,不過這也不一定。比方說 save() 和 find() 這兩個 method 其實是相輔相成的,沒有 save() 就不能測 find(),沒有 find() 也沒有辦法 assert save() 的結果。遇到這種狀況只要找一邊來測就可以了,另一個 method 則是有 "間接" 測到即可。Unit Test 並不是以 method 做為切割單位,而是以整個 class 為測試單位,讓整個 class 可以正確的運作,才是 Unit Test 的目的。
  • 開放 private 變成 public -- private 的 method 該不該測已經吵很久了,個人以為,在寫 private method 之前,先想想看如果它改成 public 會如何。個人發現讓物件多個 public 的行為其實並不會有什麼大害,如果不讓別人改變行為,加個 final 就行了啊。多個 public method 固然增加了 API 的複雜度,但要不要用是 API 使用者的事啊!想想看,ArrayList 或是 String 的 public method 夠多了吧,但你有用過它們全部的 method 嗎?你會覺得有什麼不妥或是難用嗎?重點應該擺在是物件的各種行為是不是一致、好記好學,而封不封裝成 private 到是在其次。嘿!腦筋這麼一轉,突然間海闊天空,什麼事都簡單多了 -- 可以改成 public 的就寫測試,不能改的就不用寫測試,就這樣!
  • Collection -- 遇到 Collection 時,請測試 0 個、1 個、多個 等三種組合。個人發現即使再有經驗的人,這個地方還是常出錯,尤其常常忘記處理 0 個的情況 (這就像常忘了處理 null 一樣),所以遇到 collection 時還是鐵齒一點吧... 最少測三個狀況。
  • 不測試自動產生的程式碼 -- 像是 getter/setter 和 簡單的 constructor 都可以直接用 IDE 產生,這些就不用測試了。但是,一旦程式碼是用自動產生的,那它永遠只能自動產生。也就是你不能 "手動" 去改這些 getter/setter/constructor。如果要修改,請將原來的砍掉,然後重新再產生一次。別小看這小小的一行程式,個人遇到很多次原本自動產生的碼,因為手動小改一下名字就出現 bug 了,尤其是 setter 超容易出錯!這些碼可沒 test 啊,修改之前一定要三思。
  • 有比沒有好 -- 不論測試的程式好寫難寫,品質優劣與否,或是時間真的趕到不行,最少寫一個大的 integration test,雖然不是很好,但再怎麼樣也比完全不寫強的多了。如果你力行 TDD 的話,這倒是不會發生啦。

回響

可以用 Tag <I>、<B>,程式碼請用 <PRE>