科技新知

測試場 VS 自動化測試

筆者一直地更新自己過去所編寫的程式,很恐怖的是,那時的自己很少思考過怎樣寫測試Test Case。致使每次做更新時,都膽戰心驚,要手動建立測試場,人肉去測試每個可能有受影響的地方。在那些年的時候,有能力自己搭建測試場,已經是萬幸。但當面對一些要長期維護的程式,測試場的人肉測試並不是一個有效的方法,一來費時間,二來人腦記憶並不可靠。單靠自己去想想那些地方受影響,再測試,某程度是在挑戰人腦的記憶上限。如果是團隊合作,就更麻煩,你以為修改不會影響到其他人,結果卻是翻天覆地。

所以為求長治久安,編寫自動化測試,是有必要的。這些自動化測試,都算是回歸測試,每次程式有任何地方改動,都確保所有自動化測試被通過。理想始終是理想,但實際操作又會遇到怎麼的問題?

以筆者剛更新的程式為例,難以測試主要是當初沒有想過要測試這件事,所以程式結構通常是【連續順序】地執行。想分段測試?除非先重構。

Function中太多自己創建的Object

回顧自己的程式,初期編寫時,總會我手寫我心,每想要創建任何資源,在java中就會使用 new 字眼,或是自行呼叫某些 builder 類來取得資源,這是其中一個令自己無法寫測試的原因。

我們要想想,這些資源,是不是自己Function中所關心的核心。如果這個資源是被直接回傳的,我們要保留,如果它是HttpClient,只是要來獲取其他資源的媒介,我們或許可以利用依賴注入來取得它,即是把 HttpClient 改為經呼叫方傳入。注入的好處時,我們可以在Test中,修改那些資源的行為和結果。更進一步的是,把那些資源改為 interface 的方式存取,那麼在 Test 中就能更任意地控制該資源的行為。

首次重構某些資源成為依賴注入,大部份都會影響呼叫方,很多地方都要重寫。不論使用constructor injection, setter injection, annotation injection 等,上傳呼叫方,或多或少都會要加減改變參數。極致地,我們把構建都交給Program 框架去做,例如Spring Boot中,各種資源,都交給框架去自動配致。當然,這種做法的學習成本高,除錯成本也高。

【注入】其實是想在控作那些資源,在測試中運行得到固定的行為。使用前述的HttpClient例子,當我們業務邏輯是先訪問外部Web API,再根據結果做處理,那麼我們測試時,就會想模擬Web Api的結果。如果要做到自動化測試,最強硬的手段,就修改自己的HttpClient,模疑給出固定結果。

想要做到這種,在傳統的Java中,我們需要透過進一步抽離Interface去做。但這樣做很累,所以筆者通常會用如Mockito的程式庫,去修改HttpClient的行為。有興趣直接看程式碼的讀者,可以去看 github

當然,上述的 HttpClient 例子,使用測試場也有可做測試,自己再去模擬那些Web Api的回傳,有些情況下,這樣會更真實,但大家就必需好好定義測試場的行為。因為測試場可能與團隊的其他成員所共用,有機會其他人可能想要更多互動的測試方式,而非固定的結果。但並上非固定結果的測試場,自動化要測試的可控度就減少。

馬交野