即時精選

[Testing] 寫單元測試的正確姿勢(一)

更新於 發佈於 閱讀時間約 6 分鐘

在寫程式的過程中,最讓人頭痛的就是當功能越來越複雜,修改某一塊程式碼時,突然發現別的地方出問題了。怎麼樣才能又快又好地確保程式碼沒毛病?這時候,測試就成了我們的好幫手。

很多人以為測試只是驗證程式碼的功能對不對,但其實它的作用比這更大。測試不僅可以幫我們找到問題,還可以讓程式碼變得更清晰、更好維護,甚至在未來功能變更時幫我們省下不少麻煩。

測試的價值?

  1. 確認程式碼行為:確保它做的就是我們想要的。
  2. 提早發現問題:快速定位那些可能潛伏的 bug。
  3. 讓人放心改程式碼:有了測試,重構或新增功能時不用再提心吊膽。
  4. 省時又省力:自動化測試讓我們不用再重複做繁瑣的手動測試。
  5. 設計更好的程式碼:讓結構更清楚,也更容易被接手或修改。

每種類型的測試都有不同的測試目的和覆蓋範圍,可以從不同的角度來確保軟體的品質,而PHPUnit 支持多種不同的測試類型,包括單元測試、功能測試和功能測試…等。這裡主要會講到的兩大項為Unit TestFeature Test

Unit Test(單元測試)

  • 測試範圍
    • 單一類別、方法或模組的行為。
    • 不依賴於外部系統或框架功能(如路由、中介層)。
  • Mock 的使用
    • 將所有外部依賴(例如 Repository、Service)進行 Mock。
    • 測試的重點是目標類別內的邏輯,而不是依賴的具體實現。
  • 適用場景
    • Service \ Repository
    • 驗證方法是否正確調用依賴、處理資料和回傳結果。

Feature Test(功能測試)

  • 測試範圍
    • 應用程式的完整功能流。
    • 通常測試控制器行為,包括請求、回應和中介層的協作。
  • Mock 的使用
    • 在需要模擬外部系統(如 API 請求)時使用 Mock,但大多依賴真實的資料和系統行為。
  • 適用場景
    • 測試整個測驗功能,例如:
      1. 發送請求到 Controller\ Validation
      2. 通過 Mock Service 處理邏輯。
      3. 驗證回應是否正確。

Service 屬於 Unit Test 的原因

  • 單一類別測試
    • 測試的是 Service 的方法是否能按照業務需求正確執行。
  • Mock 依賴
    • 測試中使用 Mock 的 Repository 和其他 Service,重點關注 Service 本身的邏輯。
  • 與外部系統分離
    • 測試不涉及Controller、Route或Request流程。

如何結合 Feature 和 Unit 測試

  1. Unit Test
    • Service 進行細粒度測試。
    • 模擬所有外部依賴,驗證邏輯的正確性。
  2. Feature Test
    • 測試 Controller 是否正確調用 Service
    • 驗證整個功能流,包括請求和回應。

這樣可以確保程式的不同層級都被充分測試,既有精細的單元測試,也有高層的整合測試。


單元測試的 3A 原則

  1. Arrange(準備): 設定測試所需的前置條件,包括初始化資料、模擬外部依賴等。
  2. Act(執行): 呼叫需要測試的方法或功能。
  3. Assert(驗證): 驗證執行結果是否符合預期。

如何執行測試?

使用 vendor/bin/phpunit 執行測試:

當你執行 vendor/bin/phpunit,你實際上是在執行專案本地的 PHPUnit,這個版本是根據 composer.json 安裝的特定版本。

因為可能同時開發不同版本的專案,每個專案裡面的phpunit版本不一定一樣,所以進入docker容器,在下指令會比較不會出錯。

範例:

--filter {functionName} -> 只執行某一個測試

vendor/bin/phpunit Tests/Feature/Controllers/ExamControllerTest.php --filter testRegisterUsersSuccess
  • 指定測試檔案Tests/Unit/Services/ExamServiceTest.php
  • 指定測試方法--filter testSyncMercerAssessmentUpdatesDatabase


備註:

  • 在 Windows 系統中,路徑分隔符是反斜線 (\),但由於反斜線在 CLI 中具有特殊含義(例如轉義),需要加倍反斜線來正確解析路徑。
  • 如果使用 Linux 或 macOS,可以直接使用單斜線 /

小結

測試不僅能幫助我們檢查程式是否正確運作,還能促進程式碼的結構化與可維護性。在應用程式開發中,結合 Unit Test 和 Feature Test 可以確保程式邏輯細節與整體功能流都能無誤運行。

下一篇文章將進一步深入實際測試的應用範例,尤其是如何使用 Laravel 的 Factory 來建立測試資料,並搭配具體測試案例來演示 PHPUnit 測試的執行過程。我們將從測試資料的建立到完整測試的執行步驟,帶領大家實際操作,體驗單元測試與功能測試如何在開發流程中發揮威力。

歡迎來到彼得的沙龍,在這裡,我將與你分享書籍精華的智慧、人際溝通的技巧、理財增值的秘訣,以及情緒管理的策略。不僅幫助你打好財務基礎,還能引領你在人生的每個環節中游刃有餘。如果你渴望成長,並追求更充實的生活,這裡就是你值得關注的空間。立即加入,與我一起探索成長的無限可能!
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
本文介紹了 Docker 的基本概念,包括 Docker Image、Docker Container、Dockerfile、Docker Compose 及其應用情境,如開發與測試環境、微服務架構和持續整合/持續部署。瞭解這些內容能幫助開發人員更有效地利用 Docker 進行應用程式的部署和管理。
本文探討如何利用政府提供的YouBike開放數據進行數據分析,以揭示共享單車系統的使用趨勢。作者使用Google Apps Script分析不同時間點的YouBike使用率,並通過線性回歸預測供需平衡,以及針對特定站點的使用模式進行深入挖掘。最後總結了公館不同出口的使用情況,提供了最佳的租借選擇。
在進行SQL查詢邏輯更改時,需要適當地使用SubQuery和join來達到新的排序需求。本文將介紹原本的撈取邏輯、需求以及如何使用SubQuery來解決新的排序需求。
本文介紹了 Docker 的基本概念,包括 Docker Image、Docker Container、Dockerfile、Docker Compose 及其應用情境,如開發與測試環境、微服務架構和持續整合/持續部署。瞭解這些內容能幫助開發人員更有效地利用 Docker 進行應用程式的部署和管理。
本文探討如何利用政府提供的YouBike開放數據進行數據分析,以揭示共享單車系統的使用趨勢。作者使用Google Apps Script分析不同時間點的YouBike使用率,並通過線性回歸預測供需平衡,以及針對特定站點的使用模式進行深入挖掘。最後總結了公館不同出口的使用情況,提供了最佳的租借選擇。
在進行SQL查詢邏輯更改時,需要適當地使用SubQuery和join來達到新的排序需求。本文將介紹原本的撈取邏輯、需求以及如何使用SubQuery來解決新的排序需求。
你可能也想看
Google News 追蹤
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
在實務情境上,常會有在單一專案程式庫中,存取多個不同資料庫的使用情境,在這種情況下,我們通常會設置多個資料庫連線(Database Connection)設定。 在平常開發使用設很方便,但要做測試時就會發現一些問題: 在測試程式碼或 Seeder 中調用 factory() 時,都是在預設連線資
今天就讓我們依照前一天的情境題,來撰寫測試案例函數吧! 這次同樣地,先讓我們規畫擬訂測試案例: 測試案例 使用者註冊: 使用者可送出註冊資料,系統將建立使用者資料,並送出含有專屬驗證連結之驗證信,當此驗證連結被開啟後,將讓使用者轉為已驗證狀態 請求錯誤的驗證連結: 錯誤的驗證連結被開啟後
前兩天,我們探討了「網站文章」的情境題;今明兩天,就讓我們探討另一個情境題「會員註冊」吧! 這邊我們同樣假設網站是採前後端分離的設計,因此我們就專注在測試 API 的部分,不過會多一個「註冊驗證信」的部分要實作與做測試驗證。 使用案例 使用者可填寫註冊資料後送出資料。 使用者可收到註冊驗證信
今天就讓我們依照前一天的情境題,來撰寫測試案例函數吧! 先讓我們規畫擬訂測試案例: 測試案例 當使用者瀏覽文章清單頁時: 使用者可看到所有文章清單,也就是【文章清單API】要能確實將資料庫內的文章資料,筆數不多不少地回應出來。 當使用者瀏覽單一文章頁時: 使用者可看到該文章資料,也就是【
在前面的篇幅中,與大家分享了許多撰寫 PHPUnit 測試程式碼所需的知識,之後的文章就讓我們來來模擬一些情境題,並在這些情境題底下,實際去設計測試案例函數吧! 作為第一個情境題,我們就選「網站文章」來當作第一個挑戰吧! 這邊我們假設網站是採前後端分離的設計,因此我們就專注在測試 API 的部分
今天讓我們回顧一下前一天的 Mocking 初體驗吧! Mocking 初體驗回顧 app/Repositories/UserRepository.php <?php namespace App\Repositories; use App\Models\User; class UserR
今天要來為大家介紹幾個,在撰寫測試程式碼時可以利用的特殊函數。 setUp() & tearDown() setUp():我們可以在這個函數中,撰寫想要在每個測試案例函數執行前預執行的邏輯。 tearDown():我們可以在這個函數中,撰寫想要在每個測試案例函數執行後預執行的邏輯。 範例:
在之前的文章中,我們演練了許多測試方式,不過不知道大家有沒有發現,我們測試的大多是「正向」情況,「反向」的情況反而沒有測試到,也就是例外情況。 例外情況也可以測試嗎?當然可以! 本篇文章會為大家介紹如何「成功地測試失敗」。 例外測試函數 $this->expectException() 函
在前幾篇文章中,我們介紹了測試3A原則,也介紹了許多 Assertion 函數,今天就讓我們實際演練吧! 過去的經驗中,最常用自動化測試來測式的對象,大概就是API了,而前後端分離也是目前 Web 開發界常用的模式,因此我們就以 API 測試來演練吧! 驗證HTTP Status Code H
前一天我們實作了第一個測試,我們學到了第一個 Assert 函數 assertEquals。 今天讓我們來了解其他常用的 Assert 函數吧! 通用型 Assertion 函數 assertEmpty 函數簽名:assertEmpty(mixed $actual[, string $mess
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
在實務情境上,常會有在單一專案程式庫中,存取多個不同資料庫的使用情境,在這種情況下,我們通常會設置多個資料庫連線(Database Connection)設定。 在平常開發使用設很方便,但要做測試時就會發現一些問題: 在測試程式碼或 Seeder 中調用 factory() 時,都是在預設連線資
今天就讓我們依照前一天的情境題,來撰寫測試案例函數吧! 這次同樣地,先讓我們規畫擬訂測試案例: 測試案例 使用者註冊: 使用者可送出註冊資料,系統將建立使用者資料,並送出含有專屬驗證連結之驗證信,當此驗證連結被開啟後,將讓使用者轉為已驗證狀態 請求錯誤的驗證連結: 錯誤的驗證連結被開啟後
前兩天,我們探討了「網站文章」的情境題;今明兩天,就讓我們探討另一個情境題「會員註冊」吧! 這邊我們同樣假設網站是採前後端分離的設計,因此我們就專注在測試 API 的部分,不過會多一個「註冊驗證信」的部分要實作與做測試驗證。 使用案例 使用者可填寫註冊資料後送出資料。 使用者可收到註冊驗證信
今天就讓我們依照前一天的情境題,來撰寫測試案例函數吧! 先讓我們規畫擬訂測試案例: 測試案例 當使用者瀏覽文章清單頁時: 使用者可看到所有文章清單,也就是【文章清單API】要能確實將資料庫內的文章資料,筆數不多不少地回應出來。 當使用者瀏覽單一文章頁時: 使用者可看到該文章資料,也就是【
在前面的篇幅中,與大家分享了許多撰寫 PHPUnit 測試程式碼所需的知識,之後的文章就讓我們來來模擬一些情境題,並在這些情境題底下,實際去設計測試案例函數吧! 作為第一個情境題,我們就選「網站文章」來當作第一個挑戰吧! 這邊我們假設網站是採前後端分離的設計,因此我們就專注在測試 API 的部分
今天讓我們回顧一下前一天的 Mocking 初體驗吧! Mocking 初體驗回顧 app/Repositories/UserRepository.php <?php namespace App\Repositories; use App\Models\User; class UserR
今天要來為大家介紹幾個,在撰寫測試程式碼時可以利用的特殊函數。 setUp() & tearDown() setUp():我們可以在這個函數中,撰寫想要在每個測試案例函數執行前預執行的邏輯。 tearDown():我們可以在這個函數中,撰寫想要在每個測試案例函數執行後預執行的邏輯。 範例:
在之前的文章中,我們演練了許多測試方式,不過不知道大家有沒有發現,我們測試的大多是「正向」情況,「反向」的情況反而沒有測試到,也就是例外情況。 例外情況也可以測試嗎?當然可以! 本篇文章會為大家介紹如何「成功地測試失敗」。 例外測試函數 $this->expectException() 函
在前幾篇文章中,我們介紹了測試3A原則,也介紹了許多 Assertion 函數,今天就讓我們實際演練吧! 過去的經驗中,最常用自動化測試來測式的對象,大概就是API了,而前後端分離也是目前 Web 開發界常用的模式,因此我們就以 API 測試來演練吧! 驗證HTTP Status Code H
前一天我們實作了第一個測試,我們學到了第一個 Assert 函數 assertEquals。 今天讓我們來了解其他常用的 Assert 函數吧! 通用型 Assertion 函數 assertEmpty 函數簽名:assertEmpty(mixed $actual[, string $mess