本次課題是「取得部落格文章 API」,撰寫 api/articles API 功能,並實作 api 路由測試、service 測試、repository 測試,及 mock 相依隔離練習。

前言

最近考試遇到這題,有稍微調整題目要求的內容。

練習環境

  • PHP >= 8 (PHPStorm 掛載 Docker Compose 對應的容器 PHP 版本)
  • Laravel >= 8
  • Laravel Sail

這邊因為需要安裝套件 (QueryFilter) 關係,有版本問題 (PHP >= 8),在此就改用 Laravel Sail 當作測試環境練習;

另外,一開始還沒使用套件之前,測試執行比較快 (10+ 個測試案例,幾毫秒),後來改指定用容器 PHP 版本去跑,執行總時長會略久一點點 (但還是 <1 秒)。

練習重點

以往寫測試,總是習慣直接用「呼叫該方法」,測試「回傳值」或是「資料庫資料變化 (例如測試新增,確認有沒有真的寫入)」,但寫到後來常常沒有拆解的很好,導致變成「整合測試」;想要保持「單元測試」的步調去撰寫測試,這邊自己簡單歸納幾種我覺得比較好記憶的點:

  1. 先有個基本認知:如何才算得上是「單元測試」,以下是參考自「單元測試的藝術 第二版」書中提到的觀念,歸納出來的:
    • 測回傳值,例如:取得使用者個人資料,取得已發布文章,取得待辦事項等
    • 測狀態變化,例如:將文章改為已發布,將使用者改為禁止登入等
    • 測如何互動,例如:系統發出站內信,則使用者會收到通知事件;輸入密碼錯誤,則觸發登入失敗的異常例外等
  2. 區分「單元測試」與「整合測試」的差異,可以從幾種方面去評估,一樣是參考自書中,這邊簡單歸納「整合測試」會有哪些特點:
    • 測很多東西:例如針對邏輯分支多的方法一口氣做測試 (if 怎樣 else 怎樣, switch 每個 case 怎樣)、測試的斷言多
    • 測試過程,除了測試案例外,一併測試使用到的資料庫資料 (測邏輯也測 Data)
    • 測試對象方法中,擁有的相依物件也一併測試斷言
  3. 若無邏輯區別,不要增加多餘重複性場景的測試,例如:1+1 斷言總和為 2,4+100 也測,2000+666 也測,但若輸入數值有限制不能大於 1000,就得追加測試 >1000 的案例。

先想清楚「現在要測什麼」,「想一個」案例來測,且必須跟其他測試是有意義上的區別。

題目說明

  • 呼叫 api/articles,返回所有文章
  • 呼叫 api/articles,指定文章發布時間,返回符合指定時間內的所有文章
  • 呼叫 api/articles,指定分頁數,與頁碼,返回指定頁數的文章,共 n 筆,n 為指定的分頁數量

筆記

記錄一下遇到的問題

  • 一開始一頭熱的就寫了所有情境的 Http Test ,後來想想這樣等於一口氣測試了 route > controller > service > repository,很明顯就是整合測試,直接測試取得的資料是否符合預期,雖然這也沒什麼問題,但就是沒寫到「單元測試」。
  • 整個重寫,若是用 TDD 的步調,拆解場景與案例,思考大概會怎麼實作:
1. 測 route > controller 這段,直接用 HTTP test 訪問 `api/articles`,取得 HTTP Code 200
2. 測 route > controller 這段,透過 mock 定義,應該要呼叫相關的 RepositoryInterface,表示一定要透過這個介面拿資料
3. 測 service,對輸入的 input 做特定檢查,例如 from 不能大於 to,斷言預期會有例外發生 (因為 service 無其他邏輯,主要都在對 Repository 這段做單元測試)
4. 測實作了 repository interface 的 repository 方法,測各種回傳值是否符合預期

mock 使用時機

簡單說就是寫好劇本,隔離相依物件 (例如資料庫存取),本次題目有用到 mock,特別記錄一下:

測試場景為確認呼叫 service 本身的邏輯是否正確,但並不想受到 repository interface 這段過程中邏輯所影響,則直接用 mock 模擬這段劇本。

另一個場景為,不想真的用 factory 寫資料到資料庫,在去呼叫方法,判斷返回資料下斷言;

應該要專注測試「我若符合某種條件去呼叫方法」,則「應該會有某個特定方法也會被呼叫」,例如:

  • mock Article shouldReceive find once
  • call service getArticles find by article id

最後

有任何問題或想法,歡迎留言交流,如果寫的內容有錯誤的地方,希望能不吝指點,感謝。

參考連結



文章作者: littlebookboy
永久鏈結: https://blog.genesu.me/2021/07/tdd-daily-002-tdd-unit-test-note/
許可協議: 署名-非商業性使用-相同方式共享 4.0 國際(CC BY-NC-SA 4.0)