Laravel Test 實戰:與框架結合的測試技巧

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

從 PHPUnit 到 Laravel Test

在前兩篇文章中,我們先談了為什麼「還沒開始單元測試的你一定很忙」,並介紹了如何使用純粹的 PHPUnit 從 0 到 1 寫出第一支測試。

現在,我們要走得更深、更實務:如果你是使用 Laravel 進行開發,那 Laravel 本身就整合了 PHPUnit,並且提供了許多「框架級」的測試輔助工具,讓測試前後端互動、資料庫操作、使用者身份驗證等都更加便捷。

本篇目標:帶領你認識 Laravel 的測試環境、常見的測試寫法,以及在 Laravel 專案中快速上手的技巧,讓測試不再只限於「函式級測試」,而是能全面涵蓋路由、Controller、資料庫、認證機制等。


Laravel 測試的環境設置

以下使用的版本是 Laravel 10

預設結構與設定

測試目錄:Laravel 在專案根目錄下預設有 tests/ 資料夾,一般會依照功能類型(Feature、Unit 等)進行區分。

Laravel 預設的測試目錄結構如下:

tests/
├── Feature/
│ └── ExampleTest.php
├── Unit/
│ └── ExampleTest.php
└── TestCase.php
  • Feature 測試:用於測試應用的高階行為,例如路由、Controller 和資料庫操作。
  • Unit 測試:用於測試單一功能或方法的邏輯,通常與框架無關。

你可以根據專案需求進一步擴展這個結構,例如:

tests/
├── Feature/
│ ├── Auth/
│ │ └── LoginTest.php
│ ├── ArticleTest.php
├── Unit/
│ ├── Models/
│ │ └── ArticleTest.php
└── TestCase.php

phpunit.xml:Laravel 預設會有一個 phpunit.xml 檔案,用來指定測試設定(例如測試資料庫的連線設定、Bootstrap 路徑)。

為避免測試影響開發資料,建議設定一個測試專用資料庫:

.env.testing 配置:

DB_CONNECTION=mysql
DB_DATABASE=testing_db
DB_USERNAME=root
DB_PASSWORD=

phpunit.xml 指定環境變數:

<server name="APP_ENV" value="testing"/>

執行測試:在 Laravel 專案中,你可以直接使用

或者依舊可以執行:

這兩種方式都是「同一套」,只是 artisan test 會多一些 Laravel 特有的報告樣式與懶人指令。

php artisan test
vendor/bin/phpunit

雖然 Laravel 已經幫我們「預設」了不少東西,但並不是每個專案的架構都一樣。你仍可以自訂測試目錄、切分更多測試子資料夾。這取決於團隊規模與習慣。


測試 HTTP 路由與 Controller

在 Laravel 專案中,最常見的測試場景之一,就是測試「透過 HTTP 路由呼叫 Controller 是否能得到預期結果」。Laravel 提供了一套流暢的 API 讓我們可以撰寫這類測試。

建立測試檔

假設我們要測試一個簡單的文章列表功能(ArticleController@index)。我們可以在 tests/Feature/ArticleTest.php 建立測試檔:

<?php

namespace Tests\\Feature;

use Tests\\TestCase;
use Illuminate\\Foundation\\Testing\\RefreshDatabase;
use App\\Models\\Article;

class ArticleTest extends TestCase
{
use RefreshDatabase;

public function testArticleIndexReturnsData()
{
// Arrange: 建立測試用資料
Article::factory()->count(3)->create();

// Act: 透過 GET 方式請求 /articles
$response = $this->get('/articles');

// Assert: 驗證回應狀態碼與資料
$response->assertStatus(200);
$response->assertSeeText('Title'); // 假設 Blade 模板會顯示文章標題
}
}

主要測試方法解說

  • $this->get('/articles'):模擬一個對路由 /articles 的 GET 請求,並獲得回應。
  • assertStatus(200):斷言 HTTP 回應狀態碼是否是 200 OK。
  • assertSeeText('Title'):檢查回應的內容中,是否包含「Title」字串。

這樣就能確保我們在 Controller / Blade 中渲染的資料確實包含在回應中。

RefreshDatabase 的作用

  • use RefreshDatabase;:每次測試執行前後,會自動「回復資料庫狀態」,通常是透過跑 migration 讓資料庫保持乾淨,避免測試彼此干擾。
  • 這對測試資料庫操作時非常重要,否則如果你的測試共用了同一個資料庫狀態,很可能因為前後次序或殘留資料而導致測試結果不一致。

透過具體的例子與方法呼叫,證明 Laravel Test 能輕鬆測試 Controller 與資料庫互動。只要能舉出一個實作成功的案例,就證明了它的可行性與便利性。


測試資料庫與 Model

除了直接透過路由做測試,你也可以針對 Model 的邏輯與資料庫互動進行更細微的測試。

<?php

namespace Tests\\Unit;

use Tests\\TestCase;
use App\\Models\\Article;
use Illuminate\\Foundation\\Testing\\RefreshDatabase;

class ArticleModelTest extends TestCase
{
use RefreshDatabase;

public function testArticleCreation()
{
$article = Article::factory()->create([
'title' => 'Test Title',
]);

// 斷言:資料庫有這筆資料
$this->assertDatabaseHas('articles', [
'title' => 'Test Title',
]);

// 斷言:這筆資料有自動產生的 ID
$this->assertNotNull($article->id);
}
}

  • assertDatabaseHas():檢查資料表中是否存在指定條件的紀錄。
  • 這比起寫生硬的 SQL 查詢再自己判斷要更方便,也更貼近 Laravel 的語法風格。

驗證與認證測試:actingAs()

在實際應用中,很多功能都需要使用者登入、角色驗證等。Laravel 測試中提供了 actingAs() 讓你能模擬使用者身份。

public function testUserCanSeeProfilePage()
{
$user = User::factory()->create();

// 模擬該用戶登入
$response = $this->actingAs($user)->get('/profile');

$response->assertStatus(200);
$response->assertSeeText($user->name);
}

這樣就能在測試時,直接測試「某用戶」存取某頁面是否正確,而不用真的在瀏覽器裡點擊登入流程。


更多測試技巧

  1. json() 與 API 測試
    • 如果你開發的是 API 端點,可以使用 $this->json('POST', '/api/articles', [...]),並用 assertJson()assertJsonFragment() 等方法做 JSON 回應檢查。
  2. Mock 與依賴注入
    • Laravel 的 Service Container 能搭配 PHPUnit 的 Mock 機制,模擬外部服務(例如第三方 API 呼叫),減少測試時需依賴真實服務的風險。
  3. 事件與排程測試
    • 對於觸發事件(Event)、監聽器(Listener),或是使用 Laravel Scheduler 的功能,也可以在測試中手動觸發並檢驗邏輯。

上面列舉的只是常見的測試技巧,每個專案可能會遇到更複雜的情境(例如多層次的前後端整合、大量第三方 API 介接等)。Laravel 的測試工具箱夠豐富,但仍需配合更完整的設計與規劃才能覆蓋所有情境。


測試與開發的流程建議

  1. 先寫測試,再寫功能(TDD 思維)
    • 即便你不是真的嚴格落實 TDD,先用測試描述需求,再開始開發,也能讓程式碼更聚焦、邏輯更清晰。
  2. 善用 Artisan 指令
    • php artisan make:test MyFeatureTest --unit 可以快速生成測試檔案,省去手動建立檔案、引用命名空間的麻煩。
  3. 持續執行測試
    • 在開發過程中,隨時執行 php artisan test,保持測試「綠燈」狀態;一旦出現紅燈就立刻修正。

透過持續跑測試,你可以很明顯感受到「有測試」帶來的安心感,一旦程式邏輯被破壞,測試會第一時間告訴你,比人肉檢查更有效率。


更完整的測試布局

在 Laravel 生態系中,撰寫測試不僅能驗證「程式是否正常」,也能檢查「介面與資料庫整合度」、「使用者角色驗證是否正確」、「多步驟流程互動」等等。

這些功能在純粹的 PHPUnit 環境下也做得到,但 Laravel 提供了大量的快捷方法與輔助工具,讓你不必從頭自己組裝一堆測試工具。

  • 用上述範例和方法,你就能看到 Laravel Test 的便利性,並「證明」測試在實務專案中大大提升效率。
  • 並非所有專案都會用到相同的測試技巧,也不是所有人都需依賴相同的套件或寫法。面對多元的業務需求,還是需要彈性的思考與調整。

預告下一篇:測試的思維轉換

到這裡,我們已經能運用 Laravel Test 基本的實作技巧。但測試除了技術層面,還有一個更重要的部分——如何將測試思維拓展到團隊層面?

在下一篇,我們將探討「測試的思維轉換:從個人到團隊的測試策略」。如何讓整個團隊一起落實測試,並在專案合作中形塑出穩固且有效率的開發流程?敬請期待!


文章重點回顧

  1. Laravel Test 環境與結構tests/ 資料夾、phpunit.xmlartisan test
  2. 測試 HTTP 路由與 Controllerget(), post() 等方法與 assertStatus(), assertSeeText()
  3. 測試資料庫與 ModelassertDatabaseHas()RefreshDatabase 等工具,確保資料庫狀態一致性。
  4. 使用者驗證與認證測試actingAs() 輕鬆模擬登入。
  5. 更多技巧:API 測試、Mock 依賴、事件與排程測試。
  6. 思維建議:持續測試、融入開發流程,體會有測試的安心感。

透過這些範例,希望你能漸漸培養在 Laravel 中「自然而然就想寫測試」的好習慣。下篇文章,讓我們進一步聊聊如何把測試從個人的行為,擴展到整個團隊的合作策略!

這是一系列以軟體開發為主題的輕鬆分享,內容涵蓋了技術選擇、開發經驗、實戰應用等多方面的議題。無論是如何在眾多框架中做出選擇,還是如何應對技術轉移的挑戰,這裡有幽默、有趣的對話風格,將複雜的技術問題轉化為易懂的故事。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
為什麼要學 PHPUnit? 在上一篇文章裡,我們聊到了「還沒開始單元測試的你一定很忙」,同時也鼓勵大家「一個人就能開始測試」。那麼接下來就要進入更實際的操作層面,帶你走進 PHP 最常見的測試框架 —— PHPUnit。
你是不是也有過這樣的經歷:剛修好的功能,過幾天又壞了;每次修改程式碼都得手動測試一遍,還常漏掉影響到的其他地方;整天忙到翻,但最後卻不知道忙在哪裡? 如果這些情境聽起來很熟悉,那麼你可能需要開始了解 單元測試!很多人覺得測試很難,或者認為時間不夠用,其實只要從幾個簡單的步驟開始,一個人也能輕鬆上手。
在一個人資(HR)系統中,薪資結算與保費扣除是最核心的功能之一,扣員工的錢不能多算,該給政府的的也不能少算…。 不管是接手舊系統,還是開發新系統,只要隨著時間推移,每年勞健保的投保級距與費率都可能調整,
「嘿,你有聽說 PHP 8.4 的新特性了嗎?」新特性能在存取屬性時進行驗證、格式化,並支援虛擬屬性設計,程式碼更簡潔易讀,有助於維護和擴展。開發者可透過此特性快速實現自訂邏輯,同時保有工具相容性,讓整體開發流程更順暢、直觀。
CodeIgniter 3 和 Laravel 是兩種不同的 PHP 框架,各有其特點和適用場景。CodeIgniter 3 是一個輕量級框架,Laravel 是一個功能強大的現代 PHP 框架,同樣都有Models的它們有什麼樣的差別呢?
本篇文章為Laravel初學者提供了一個指南,深入探討了Laravel的routes目錄下的功能。文章詳細描述了web.php和api.php的差異和使用情境,並簡要介紹了console.php和channels.php的功能。透過這篇文章,讀者可以更好地理解和利用Laravel的路由功能。
為什麼要學 PHPUnit? 在上一篇文章裡,我們聊到了「還沒開始單元測試的你一定很忙」,同時也鼓勵大家「一個人就能開始測試」。那麼接下來就要進入更實際的操作層面,帶你走進 PHP 最常見的測試框架 —— PHPUnit。
你是不是也有過這樣的經歷:剛修好的功能,過幾天又壞了;每次修改程式碼都得手動測試一遍,還常漏掉影響到的其他地方;整天忙到翻,但最後卻不知道忙在哪裡? 如果這些情境聽起來很熟悉,那麼你可能需要開始了解 單元測試!很多人覺得測試很難,或者認為時間不夠用,其實只要從幾個簡單的步驟開始,一個人也能輕鬆上手。
在一個人資(HR)系統中,薪資結算與保費扣除是最核心的功能之一,扣員工的錢不能多算,該給政府的的也不能少算…。 不管是接手舊系統,還是開發新系統,只要隨著時間推移,每年勞健保的投保級距與費率都可能調整,
「嘿,你有聽說 PHP 8.4 的新特性了嗎?」新特性能在存取屬性時進行驗證、格式化,並支援虛擬屬性設計,程式碼更簡潔易讀,有助於維護和擴展。開發者可透過此特性快速實現自訂邏輯,同時保有工具相容性,讓整體開發流程更順暢、直觀。
CodeIgniter 3 和 Laravel 是兩種不同的 PHP 框架,各有其特點和適用場景。CodeIgniter 3 是一個輕量級框架,Laravel 是一個功能強大的現代 PHP 框架,同樣都有Models的它們有什麼樣的差別呢?
本篇文章為Laravel初學者提供了一個指南,深入探討了Laravel的routes目錄下的功能。文章詳細描述了web.php和api.php的差異和使用情境,並簡要介紹了console.php和channels.php的功能。透過這篇文章,讀者可以更好地理解和利用Laravel的路由功能。
你可能也想看
Google News 追蹤
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
今天就讓我們依照前一天的情境題,來撰寫測試案例函數吧! 這次同樣地,先讓我們規畫擬訂測試案例: 測試案例 使用者註冊: 使用者可送出註冊資料,系統將建立使用者資料,並送出含有專屬驗證連結之驗證信,當此驗證連結被開啟後,將讓使用者轉為已驗證狀態 請求錯誤的驗證連結: 錯誤的驗證連結被開啟後
今天就讓我們依照前一天的情境題,來撰寫測試案例函數吧! 先讓我們規畫擬訂測試案例: 測試案例 當使用者瀏覽文章清單頁時: 使用者可看到所有文章清單,也就是【文章清單API】要能確實將資料庫內的文章資料,筆數不多不少地回應出來。 當使用者瀏覽單一文章頁時: 使用者可看到該文章資料,也就是【
在前面的篇幅中,與大家分享了許多撰寫 PHPUnit 測試程式碼所需的知識,之後的文章就讓我們來來模擬一些情境題,並在這些情境題底下,實際去設計測試案例函數吧! 作為第一個情境題,我們就選「網站文章」來當作第一個挑戰吧! 這邊我們假設網站是採前後端分離的設計,因此我們就專注在測試 API 的部分
今天讓我們來看 phpunit.xml 吧! phpunit.xml 位在 Laravel 專案根目錄底下,顧名思義,它是一個設定 PHPUnit 執行方式的設定XML檔,PHPUnit 提供了不少設定值可供設定,這邊只提最重要的幾個: stopOnFailure 說明:當此欄位設定為 tru
今天我們會接續環繞著 「Mocking」 這個主題。 在 Laravel 中,有幾個類別的 Mocking 方式,與前兩天所介紹的方式有所不同,在這次的系列文章中,會和大家介紹 Event、Mail、Queue、Storage、HTTP 這幾個類別的特殊 Mocking 方式。 今天就先來看 E
今天讓我們回顧一下前一天的 Mocking 初體驗吧! Mocking 初體驗回顧 app/Repositories/UserRepository.php <?php namespace App\Repositories; use App\Models\User; class UserR
指令在現代 Laravel Web Applications 中,也是一個相當常見的應用,而 Laravel 也為此準備許多方便實現測試的函數,以下就來為大家介紹: artisan() 函數簽名: artisan($command, $parameters = []) 函數說明:這應該是指令測
在之前的文章中,我們分別演練了 API 測試與資料庫測試,今天則讓我們停下腳步,來介紹一些 Laravel 提供的,與 自動化測試有關的 Trait 吧! DatabaseMigrations 當我們使用了這個 Trait 後,會在每個測試被執行前,先執行 migrate ,接著在測試被執行後,
今天會再與大家介紹幾個資料庫 Assertion 函數,與陣列 Assertion 函數。 與前一篇一樣,以下會提到的資料庫 Assertion 函數,並非 PHPUnit 內建,而是由 Laravel 所擴充,因此需注意是否有確實引用到 use Tests\\TestCase 。最後面介紹的2個
前一天我們實作了第一個測試,我們學到了第一個 Assert 函數 assertEquals。 今天讓我們來了解其他常用的 Assert 函數吧! 通用型 Assertion 函數 assertEmpty 函數簽名:assertEmpty(mixed $actual[, string $mess
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
今天就讓我們依照前一天的情境題,來撰寫測試案例函數吧! 這次同樣地,先讓我們規畫擬訂測試案例: 測試案例 使用者註冊: 使用者可送出註冊資料,系統將建立使用者資料,並送出含有專屬驗證連結之驗證信,當此驗證連結被開啟後,將讓使用者轉為已驗證狀態 請求錯誤的驗證連結: 錯誤的驗證連結被開啟後
今天就讓我們依照前一天的情境題,來撰寫測試案例函數吧! 先讓我們規畫擬訂測試案例: 測試案例 當使用者瀏覽文章清單頁時: 使用者可看到所有文章清單,也就是【文章清單API】要能確實將資料庫內的文章資料,筆數不多不少地回應出來。 當使用者瀏覽單一文章頁時: 使用者可看到該文章資料,也就是【
在前面的篇幅中,與大家分享了許多撰寫 PHPUnit 測試程式碼所需的知識,之後的文章就讓我們來來模擬一些情境題,並在這些情境題底下,實際去設計測試案例函數吧! 作為第一個情境題,我們就選「網站文章」來當作第一個挑戰吧! 這邊我們假設網站是採前後端分離的設計,因此我們就專注在測試 API 的部分
今天讓我們來看 phpunit.xml 吧! phpunit.xml 位在 Laravel 專案根目錄底下,顧名思義,它是一個設定 PHPUnit 執行方式的設定XML檔,PHPUnit 提供了不少設定值可供設定,這邊只提最重要的幾個: stopOnFailure 說明:當此欄位設定為 tru
今天我們會接續環繞著 「Mocking」 這個主題。 在 Laravel 中,有幾個類別的 Mocking 方式,與前兩天所介紹的方式有所不同,在這次的系列文章中,會和大家介紹 Event、Mail、Queue、Storage、HTTP 這幾個類別的特殊 Mocking 方式。 今天就先來看 E
今天讓我們回顧一下前一天的 Mocking 初體驗吧! Mocking 初體驗回顧 app/Repositories/UserRepository.php <?php namespace App\Repositories; use App\Models\User; class UserR
指令在現代 Laravel Web Applications 中,也是一個相當常見的應用,而 Laravel 也為此準備許多方便實現測試的函數,以下就來為大家介紹: artisan() 函數簽名: artisan($command, $parameters = []) 函數說明:這應該是指令測
在之前的文章中,我們分別演練了 API 測試與資料庫測試,今天則讓我們停下腳步,來介紹一些 Laravel 提供的,與 自動化測試有關的 Trait 吧! DatabaseMigrations 當我們使用了這個 Trait 後,會在每個測試被執行前,先執行 migrate ,接著在測試被執行後,
今天會再與大家介紹幾個資料庫 Assertion 函數,與陣列 Assertion 函數。 與前一篇一樣,以下會提到的資料庫 Assertion 函數,並非 PHPUnit 內建,而是由 Laravel 所擴充,因此需注意是否有確實引用到 use Tests\\TestCase 。最後面介紹的2個
前一天我們實作了第一個測試,我們學到了第一個 Assert 函數 assertEquals。 今天讓我們來了解其他常用的 Assert 函數吧! 通用型 Assertion 函數 assertEmpty 函數簽名:assertEmpty(mixed $actual[, string $mess