為什麼要測試?

因為我們沒辦法保證程式碼是萬無一失的,所以通常我們都會需要驗證我們的程式,而測試就是其中一種方法。很多工程師都在寫完程式之後,自己手動去做測試;如果執行結果是按照自己的想法出現後,那麼他們通常就認為程式沒錯了。

可是,其實這樣的程式碼通常就會躲進很多很多的 Bug 。所以我們要讓程式碼自己告訴我們它沒問題,而不是我們自己去想像它沒問題

當然測試並不是萬靈丹,它只是去驗證我們所考慮到的狀況;但如果抱持著「既然我都有考慮到,而且也寫在程式碼裡,那這樣就不用測試了呀!」的態度,那其實也是災難的根源。因為如果我們沒有寫測試來驗證程式碼的話,那我們的防範措施根本就不值得信賴!

所以不是手動測試過沒問題,下次就不會再發生了;而是要拿出保證來才值得相信,而測試就是我們的保證。

如何確保系統照著規格實作?

符合客戶所要求的規格,就是我們實作系統的最終目標。有些規格是流程的控制,有些規格是資料的處理;但這些如果沒有一套好的檢驗機制來協助我們確認這些規格的話,我們就沒辦法有百分之百的信心告訴客戶說,我們的程式完成了你們的需求。

自動化測試就是一個方便的檢驗機制,而測試案例就是客戶的規格。當我們的程式通過測試後,我們也就可以放心把這些程式碼用在實際的開發中;如果這時還發現執行的結果有問題的話,通常就就可以判斷是實際開發的程式碼中出現了錯誤。

當然有些工程師會認為:為什麼我要把同樣的程式碼在測試案例和實作運作的系統中各寫一遍?其實這是錯誤的觀念。

測試案例分成兩種:一種是在程式正常運作下的案例,它們能明確告訴我們程式的使用方式,也就是客戶要求的規格;一種是設想到可能會讓程式出現問題的案例,它們考驗著身為工程師應該去設想到的錯誤處理機制。

當客戶新增或修改了規格時,那麼我們的測試案例就要跟著調整。而沒有調整的部份,我們就能透過現有的測試案例來確保舊有程式按照舊的規格執行,確保我們的程式沒有因為新增或修改的功能而出現預料之外的錯誤。

怎麼寫出好測試?

好的測試案例依賴著工程師的經驗,經驗越豐富的工程師,就能預想到各種可能發生的狀況,並寫出因應的測試案例。不過有經驗的工程師畢竟是少數,所以大多數時候我們還是要按步就班,從我們能做得到的部份做起。

一開始我們可以先對正常的流程編寫測試;而在程式通過正常測試之後,我們再針對有可能造成我們程式誤動作的狀況去加入異常的測試。有時候 Bug 可能是因為操作流程錯誤而產生,那麼就要在流程中的每個關卡做好程序的把關。

例如某些關卡要有防呆機制,某些關卡不能重覆執行,我們就必須去防止等等。測試之所以要強調自動化,就是讓我們不用把時間花在手動去模擬使用者操作的過程;而且我們可以透過對調測試案例裡的每個觸發條件的執行順序來產生不同的測試方式,以找出可能潛藏的流程問題。

有時候我們的系統可能會有多個使用者同時在操作,如何讓獨立性的資料不互相干擾,或是避免資料更新的先後順序問題 (例如商品庫存) ,這都是好的測試應該注意的重點。

重要的是,每個測試案例是要有意義的,我們在撰寫測試之前就要先分析它們的前因後果,才能擬定出正確的測試方向。如果覺得自己寫的流程測試可能有盲點時,那麼也可以請伙伴幫我們一起思考可能的狀況。

測試太花時間?

在很多 Web 系統開發的過程中,其實很少有人會去考慮測試這件事情;主要是因為 Web 系統中大部份只是簡單的 CRUD 而已,加上時程的壓力,就會讓很多開發人員放棄撰寫自動化測試。

不過在一個較具規模的系統的規劃與設計過程中,就應該將自動化測試考慮進來;而且即便有時程壓力,也應該對核心功能撰寫測試案例。尤其當核心功能牽扯到有關金流等不容有錯的部份時,更是應該用測試來嚴格把關。

更好的方式是將撰寫測試的時間在一開始就放到時程規劃裡,其他的就讓專案經理去傷腦筋,即便是在維護階段時也是如此。因為如果多花半個小時寫自動化測試的話,那麼未來找 Bug 的時間就至少可以減掉半天。

既有的程式該如何測試?

如果大部份已經在運作的程式沒有對應的自動化測試,而在它們發生問題就要花很多時間來除錯時,就應該考慮安排時間進行重構與撰寫測試。

首先在第一次重構時,可以先考慮把核心邏輯獨立出來;如果這個動作在執行上相當困難的話,那麼就先將資料或網路存取的部份獨立出來。這些外在因素我們可以透過 Dependency Injection 的方式來重構,然後在自動化測試時用 Stub 物件來代替它們。

當程式重構至無關外在變因,且手動測試通過後,就可以開始考慮撰寫自動化測試;這時還是要按照「先測正常流程,再測異常流程」的步驟來進行。而其中測試用的假資料可以從現有的資料中產生,這些資料因為已經通過客戶的驗證,更能確保測試的可靠性。

另外有些既有的錯誤是必須在長時間才能發現,這時候我們可能就要用推測的方式來撰寫測試案例。這樣一來有兩個好處,其一是我們就可以讓測試自動化去代替手動測試的部份,其二是如果未來還是發生同樣問題時,我們也能透過這些測試來排除掉先前推測出來的因素。

結論

其實我們也很難百分之百地保證通過測試的程式碼一定完全沒問題,但很重要的一點就是我們至少能保證通過測試的部份是會按照我們的想法來執行。

一個好的系統在交到客戶手上時,就應該要有一定的品質;而品質的保證來自我們的信心。至於我們的信心並不是建構在想像之上,而是應該用實際的測試數據來做為它的基礎。