Pexels Goumbik 574071

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

從 PHPUnit 到 Laravel Test

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

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

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


Laravel 測試的環境設置

以下使用的版本是 Laravel 10

預設結構與設定

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

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

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

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

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

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

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

.env.testing 配置:

YAML
DB_CONNECTION=mysql
DB_DATABASE=testing_db
DB_USERNAME=root
DB_PASSWORD=

phpunit.xml 指定環境變數:

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

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

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

Bash
php artisan test
Bash
vendor/bin/phpunit

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


測試 HTTP 路由與 Controller

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

建立測試檔

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

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
<?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() 讓你能模擬使用者身份。

PHP
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 中「自然而然就想寫測試」的好習慣。下篇文章,讓我們進一步聊聊如何把測試從個人的行為,擴展到整個團隊的合作策略!