保羅的學習筆記

Day 28 - 為程式撰寫單元測試

大多時候,我們花許多時間在開發功能,隨著功能越來越多,功能之間也互相影響。有時候,我們改了一個功能,另外一個功能卻壞了,但是我們卻沒發現。如果有 QA 幫忙測試,或許還能透過專業的測試手法找到,萬一今天只有我們自己測試,而我們又沒花太多時間測試,就會把 Bug release 出去了。

為此,我們可以進行單元測試,測試我們開發的功能。當我們每次新增功能,也為他加上測試,這些測試就能保護我們的功能,在我們因為新增其他功能而改壞或者重構有問題時,就會在我們執行時讓我們知道,哪些地方改壞了。

開始測試

在這邊就讓我們測試一下 Day 8 的 SelectedPhotos 類別的 select 方法。

Article image

從這個類別中,我們可以看出 select 有幾個行為,並為其列下測試案例:

  1. 當 photo 未被選擇,也沒超過限制時,就會能成功選擇 photo
  2. 當 photo 被選擇時,呼叫 select 會取消選擇
  3. 當選擇 photo 檔案大小總和超過 250 時,就會丟出 OverLimitException
  4. 當選擇 photo 超過 5 張時,就會丟出 OverLimitException

測試第一個案例

在 Flutter 中,我們會把測試寫在 main 方法中,並在 main 方法中放入單元測試。在單元測試中,透過實現 3A 原則:Arrange、Act、和 Assert,從準備 photo 資料、實際執行 select 方法,最後確認 photo 已經被選擇。當我們完成每一個步驟後,執行測試就從 IDE 看到一個測試通過了。

Article image

如果讀者們有興趣,也可以自己嘗試第二個測試案例。讓我們跳過第二個,嘗試測試一下非正常的流程。

測試非正常流程

當選擇的總檔案大小超過 250 時,我們執行 select 時,就會拋出一個 OverLimitException。如果我們直接在測試中執行 selectedPhotos.select,測試就會因為 OverLimitException 被拋出而失敗。所以在這測試案例中,我們不能直接呼叫 select 方法。而是必須使用 callback 的方式,把呼叫 select 的工作傳給,expect 方法,並讓 expect 方法幫我們檢查是否 select 方法有正確的拋出 OverLimitException。

Article image

同樣的,如果讀者們有興趣,也可以自己嘗試第四個測試案例。

設定假資料

有些時候,我們測試目標會相依於其他類別,與其他類別互動,最後完成工作。像是下面例子中 NewsRepository 並不會自己打 API,而是透過 HttpProvider 呼叫。在單元測試中,我們希望能避免直接呼叫真的 API 或使用 DB 等外部資源,我們必須做假這些互動。

Article image

在這些測試中,我們會使用 mock 套件來幫助我們做假這些互動,讓我們更好測試。在 Flutter 中,我們可以選擇使用 mockito 或 mocktail 來幫助我們做假。在這邊,我們使用 mocktail 來示範。

Article image

雖然這個測試與 selectedPhotos 的測試看起來不太一樣,但其實還是符合 3A 原則的。

  1. Arrange:透過 mocktail 的 API 來做假 HttpProvider 的回傳資料
  2. Act:呼叫 NewsRepository.get
  3. Assert:驗證 NewsRepository.get 的回傳資料使否符合預期

測試也需要重構

由於測試方法裡頭充滿了細節,讓我們可能不太好看出測試的流程。所以我們應該也要對測試進行重構,讓測試也具備可讀性。如此一來,當測試失敗時,我們才不會花一大堆時間看懂測試,然後才知道什麼東西出錯了。

Article image

當我們重構測試之後,測試也從長長一串,變短一些,也隱藏一些測試實作的細節,讓我們能更專注在測試的流程上。

當測試很難寫時

有些時候,我們會發現我們很難進行單元測試,有些時候是需要 mock 太多東西,有些時候是為了測試,需要準備很多資料。其實當我們發現測試很難寫時,也可能表示類別的設計有問題,可能是職責太多,也可能是直接使用了靜態外部套件,這些問題都會讓我們測試時遇到很多困難。

所以當我們發現不好測試時,應該適時的檢視當前設計,看看是否應該把類別的職責再拆小一點,或者其他各種方式,提升程式的可測試性。

結論

單元測試看起來雖然簡單,但其實並不容易。我們在這篇文章中,簡單介紹了測試時會需要的東西,並未討論深入討論各種關於單元測試的知識,例如:各種測試替身,和如何設計測試案例 …等。關於這個議題推薦大家去閱讀各路大神的文章,了解更多關於單元測試的知識。

— 全文完 —

如果這篇對你有幫助,可以分享給朋友,或追蹤後續更新。我在 Threads 也會貼一些零碎想法。