替Model寫單元測試可以幹嘛?

單元測試( Unit Test)應用在軟體開發各個領域,本文僅討論MVC架構中的Model。

在web的MVC架構中,我們開發時本來就不斷在test。

  • 你一直對網頁按重新整理就是在test
  • 你在剛寫好的頁面上輸入一些測資然後按確定,也是在test你的controller或model

也就是testing的工作你其實一直都在做。

而MVC架構中的Model特別難處理,因為Model的行為很難測。

在資料到達Model之前,會先經過View、Controller,所以你會來回狂測,想確定bug到底在哪。

是jQuery在抓網頁元素的value時抓錯?是JavaScript相關的code有bug?是html的某個form name打錯?是Controller拿到form POST的值時做了變數型態轉換?是Controller裡面變數名稱打錯?是丟變數給Model時順序丟錯?還是Model裡面有bug?

反覆跑完以上流程抓出bug就算了,偏偏並非每個功能都是直接在網頁上key完資料就丟給Model。

結果光是想丟測資給Model就很麻煩。尤其你修好一個method常會想確認其他method有沒有因此壞掉。

行為單純的Model就算了,以上過程還不會花掉太多時間。複雜一點的 Model真的是debug到我會在電腦前大罵髒話。針對Model寫單元測試等於在開發Controller跟View前,先把Model弄得很強壯。

為了搞清楚自己的Model有多健康,你會寫好幾個test、丟好幾筆測資。加上為了要讓model能被test,你會來回去修改、把model寫的更模組化、更testable 、讓使用Model的人用得更愉快。

然後使用Model的人就是你自己。

所以unit test寫完、測完,你再寫controller跟view的時候就快樂到爆。

因為你知道寫Model的那傢伙很猛、把它寫得很強悍(亂丟資料也不會怎樣,那個Model會丟Exception、會丟error、會吐訊息, 反正Model會強力守住最後一道關卡,資料庫絕對不會爛掉。)

所以你Controller跟View就可以輕鬆寫,寫超快、超隨便也沒差,頂多就是使用者key錯資料會看到exception或是一些怪怪的錯誤訊息,絕對不會有資料庫存到壞掉的資料或是資料一致性的問題出現。

開發上出現bug時,你也可以確定是controller或是view的資料處理出錯,千錯萬錯絕不是Model的錯。(那真的是Model有bug怎麼辦?回去再寫幾個test抓bug,然後修好就行囉)

然後我認為,單元測試也不需要把所有可能的測資、全部使用情境都測過。

每個method隨便寫幾個最笨的testing就很夠了,通常有bug就是return value完全跟預期不一樣。寫幾個笨testing就夠抓出一堆麻煩了。

像是:

  • 資料庫某table在新增完有沒有多一筆
  • 各欄位金額加總有沒有等於總金額欄位
  • 那個要抓10筆資料的method最後抓到的是不是10筆
  • 亂丟變數給某method有沒有吐Exception

只測試這些夠混吧?夠不嚴謹吧?但是你最常搞出來的bug就是這麼單純而已。

當這些testing都跑過了,你的Model已經非常頭好壯壯了,超健康。眼看自己動手去挑戰自己的Model,最後還全部過關,絕對會信心UP、UP。處理複雜的Model時,將省下很多時間。(就算不寫單元測試,你也是用瀏覽器東測西測、花時間抓bug不是嗎?)

好,做個整理。

時機

某Model光用想就覺得頭痛,開發起來你覺得很沒安全感

好處

  • 讓你跳出開發者的角度,從使用者的角度去看這些method好不好用、 直不直覺,因此能寫出更出色(reusability, maintainability, scalability)的Model
  • 每次改完某method,都能立刻檢查其他的method有沒有被搞壞
  • 更好抓bug(寫testing時會在Model狂抓bug。之後寫controller/view時,你會知道bug都在controller/view裡面,不用跳來跳去檢查一堆code)

所以快找幾個能用的工具,試著寫單元測試看看吧。

還是不知道從何寫起的朋友可以在下方留言,我拿最近寫的程式碼寫一篇實作的範本。(我是以PHP/Codeigniter以及Codeigniter內建的單元測試函式庫當作工具)

(Photo via  Robert E. Kennedy Library at Cal Poly, CC License)