從 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);
}
這樣就能在測試時,直接測試「某用戶」存取某頁面是否正確,而不用真的在瀏覽器裡點擊登入流程。
更多測試技巧
json()
與 API 測試- 如果你開發的是 API 端點,可以使用
$this->json('POST', '/api/articles', [...])
,並用assertJson()
、assertJsonFragment()
等方法做 JSON 回應檢查。
- 如果你開發的是 API 端點,可以使用
- Mock 與依賴注入
- Laravel 的 Service Container 能搭配 PHPUnit 的 Mock 機制,模擬外部服務(例如第三方 API 呼叫),減少測試時需依賴真實服務的風險。
- 事件與排程測試
- 對於觸發事件(Event)、監聽器(Listener),或是使用 Laravel Scheduler 的功能,也可以在測試中手動觸發並檢驗邏輯。
上面列舉的只是常見的測試技巧,每個專案可能會遇到更複雜的情境(例如多層次的前後端整合、大量第三方 API 介接等)。Laravel 的測試工具箱夠豐富,但仍需配合更完整的設計與規劃才能覆蓋所有情境。
測試與開發的流程建議
- 先寫測試,再寫功能(TDD 思維)
- 即便你不是真的嚴格落實 TDD,先用測試描述需求,再開始開發,也能讓程式碼更聚焦、邏輯更清晰。
- 善用 Artisan 指令
php artisan make:test MyFeatureTest --unit
可以快速生成測試檔案,省去手動建立檔案、引用命名空間的麻煩。
- 持續執行測試
- 在開發過程中,隨時執行
php artisan test
,保持測試「綠燈」狀態;一旦出現紅燈就立刻修正。
- 在開發過程中,隨時執行
透過持續跑測試,你可以很明顯感受到「有測試」帶來的安心感,一旦程式邏輯被破壞,測試會第一時間告訴你,比人肉檢查更有效率。
更完整的測試布局
在 Laravel 生態系中,撰寫測試不僅能驗證「程式是否正常」,也能檢查「介面與資料庫整合度」、「使用者角色驗證是否正確」、「多步驟流程互動」等等。
這些功能在純粹的 PHPUnit 環境下也做得到,但 Laravel 提供了大量的快捷方法與輔助工具,讓你不必從頭自己組裝一堆測試工具。
- 用上述範例和方法,你就能看到 Laravel Test 的便利性,並「證明」測試在實務專案中大大提升效率。
- 並非所有專案都會用到相同的測試技巧,也不是所有人都需依賴相同的套件或寫法。面對多元的業務需求,還是需要彈性的思考與調整。
預告下一篇:測試的思維轉換
到這裡,我們已經能運用 Laravel Test 基本的實作技巧。但測試除了技術層面,還有一個更重要的部分——如何將測試思維拓展到團隊層面?
在下一篇,我們將探討「測試的思維轉換:從個人到團隊的測試策略」。如何讓整個團隊一起落實測試,並在專案合作中形塑出穩固且有效率的開發流程?敬請期待!
文章重點回顧
- Laravel Test 環境與結構:
tests/
資料夾、phpunit.xml
、artisan test
。 - 測試 HTTP 路由與 Controller:
get()
,post()
等方法與assertStatus()
,assertSeeText()
。 - 測試資料庫與 Model:
assertDatabaseHas()
與RefreshDatabase
等工具,確保資料庫狀態一致性。 - 使用者驗證與認證測試:
actingAs()
輕鬆模擬登入。 - 更多技巧:API 測試、Mock 依賴、事件與排程測試。
- 思維建議:持續測試、融入開發流程,體會有測試的安心感。
透過這些範例,希望你能漸漸培養在 Laravel 中「自然而然就想寫測試」的好習慣。下篇文章,讓我們進一步聊聊如何把測試從個人的行為,擴展到整個團隊的合作策略!