iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0

「測試都通過了,為什麼上線還是出問題?」

你寫了完美的單元測試,整合測試也都綠燈,但使用者還是回報:「我點了按鈕,什麼事都沒發生!」這時你才發現,原來是路由設定寫錯了一個參數...

這就是為什麼我們需要 E2E(End-to-End)測試!今天,讓我們預覽這個強大的測試武器。

🗺️ 我們的 TDD 旅程

基礎測試 → Kata 實戰 → 框架特色 → 整合部署
  1-10        11-17       18-27       28-30

                            ↓ 我們在這裡(Day 27)🎬
[=============================================>....]  

經過 26 天的學習,我們已經建立了完整的測試金字塔。今天要站在金字塔頂端,俯瞰整個測試版圖!

🎭 E2E 測試是什麼?

想像一下,你是一個真實的使用者:

  1. 打開瀏覽器
  2. 輸入網址
  3. 點擊按鈕
  4. 填寫表單
  5. 檢查結果

E2E 測試就是模擬這整個過程!

測試金字塔回顧 🏔️

        /\
       /E2E\      ← 今天的主角!
      /------\
     /整合測試\
    /----------\
   /  單元測試   \
  /--------------\
  • 單元測試:快速、獨立、大量(Day 1-10 學習)
  • 整合測試:模組間互動(Day 25 學習)
  • E2E 測試:完整使用者流程(今天預覽)

🚀 E2E 測試工具選擇

Laravel Dusk - 瀏覽器自動化框架

Laravel 提供了 Dusk 套件來進行瀏覽器自動化測試:

安裝 Laravel Dusk

composer require laravel/dusk --dev
php artisan dusk:install

建立 tests/Browser/TodoE2ETest.php

<?php

namespace Tests\Browser;

use App\Models\User;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class TodoE2ETest extends DuskTestCase
{
    protected User $user;

    protected function setUp(): void
    {
        parent::setUp();
        $this->user = User::factory()->create();
    }

    /** @test */
    public function userCanCompleteTodoWorkflow()
    {
        $this->browse(function (Browser $browser) {
            $browser->loginAs($this->user)
                    ->visit('/todos')
                    ->assertSee('My Todo List')
                    ->type('input[name=title]', '完成 E2E 測試')
                    ->press('Add Todo')
                    ->waitForText('完成 E2E 測試')
                    ->click('.todo-checkbox')
                    ->waitFor('.completed')
                    ->click('.delete-btn')
                    ->waitUntilMissing('.todo-item')
                    ->assertDontSee('完成 E2E 測試');
        });
    }
}

這個測試完整模擬了:登入 → 新增 → 完成 → 刪除

🔍 進階測試技巧

與 API 測試的對比

// API 層級測試
test('canCreateTodoViaApi', function () {
    $response = $this->postJson('/api/todos', [
        'title' => '新增待辦'
    ]);
    
    $response->assertStatus(201);
});

E2E 測試範例

// 完整用戶體驗測試
public function userCanCreateTodoThroughUi()
{
    $this->browse(function (Browser $browser) {
        $browser->visit('/todos')
                ->type('#todo-input', '新增待辦')
                ->press('Add')
                ->assertSee('新增待辦');
    });
}

差異:API 測試驗證後端邏輯,E2E 測試驗證完整用戶體驗。

🎯 實戰案例:購物車流程

建立 tests/Browser/ShoppingFlowTest.php

<?php

namespace Tests\Browser;

use App\Models\Product;
use App\Models\User;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class ShoppingFlowTest extends DuskTestCase
{
    protected User $user;
    protected Product $product;

    protected function setUp(): void
    {
        parent::setUp();
        
        $this->user = User::factory()->create([
            'email' => 'test@example.com'
        ]);
        
        $this->product = Product::factory()->create([
            'name' => 'Laravel 測試指南',
            'price' => 299
        ]);
    }

    /** @test */
    public function completeShoppingFlow()
    {
        $this->browse(function (Browser $browser) {
            $browser
                // 瀏覽商品
                ->visit('/')
                ->click("@product-{$this->product->id}")
                ->assertSee($this->product->name)
                
                // 加入購物車
                ->press('加入購物車')
                ->waitForText('已加入購物車')
                
                // 結帳流程
                ->click('@cart-icon')
                ->press('前往結帳')
                ->type('email', 'test@example.com')
                ->type('password', 'password')
                ->press('登入')
                ->waitForLocation('/checkout')
                
                // 填寫配送
                ->type('address', '台北市測試路 123 號')
                ->press('確認訂單')
                ->waitForText('訂單完成');
        });
    }
}

常見 E2E 測試場景

用戶註冊流程

test('newUserRegistrationFlow', function () {
    $this->browse(function (Browser $browser) {
        $browser->visit('/register')
                ->type('name', 'John Doe')
                ->type('email', 'john@example.com')
                ->type('password', 'password')
                ->type('password_confirmation', 'password')
                ->check('terms')
                ->press('註冊')
                ->waitForLocation('/dashboard')
                ->assertAuthenticated();
    });
});

最佳實踐

1. 頁面物件模式

建立可重用的頁面物件:

<?php

namespace Tests\Browser\Pages;

use Laravel\Dusk\Browser;
use Laravel\Dusk\Page;

class TodoPage extends Page
{
    public function url()
    {
        return '/todos';
    }
    
    public function elements()
    {
        return [
            '@add-input' => 'input[name=title]',
            '@add-btn' => 'button.add-todo',
            '@todo-list' => '.todo-list',
        ];
    }
    
    public function addTodo(Browser $browser, $title)
    {
        $browser->type('@add-input', $title)
                ->press('@add-btn')
                ->waitForText($title);
    }
}

使用頁面物件:

test('manageTodosUsingPageObject', function () {
    $this->browse(function (Browser $browser) {
        $browser->visit(new TodoPage)
                ->addTodo('使用頁面物件')
                ->assertSeeIn('@todo-list', '使用頁面物件');
    });
});

2. 等待策略

// 等待元素出現
$browser->waitFor('.modal');

// 等待文字
$browser->waitForText('載入完成');

// 等待消失
$browser->waitUntilMissing('.loading');

// 自定義等待
$browser->waitUsing(10, 1, function () use ($browser) {
    return $browser->element('.result')->getText() === '成功';
});

🚨 常見陷阱與解決方案

1. 測試不穩定(Flaky Tests)

// ❌ 錯誤:依賴固定等待時間
test('bad practice', function () {
    $this->browse(function (Browser $browser) {
        $browser->click('button')
                ->pause(1000)  // 避免使用
                ->assertVisible('.result');
    });
});

// ✅ 正確:等待特定條件  
test('good practice', function () {
    $this->browse(function (Browser $browser) {
        $browser->click('button')
                ->waitFor('.result')
                ->assertVisible('.result');
    });
});

🎁 重點回顧

今天我們預覽了 E2E 測試的強大功能:

✅ 學到的技能

  1. E2E 測試概念:理解測試金字塔頂端
  2. Laravel Dusk 設定:建立 E2E 測試環境
  3. 實作技巧:Page Object Model
  4. 視覺測試:瀏覽器自動化
  5. 效能整合:結合效能監控

🔄 測試策略總結

單元測試 → 快速回饋、大量覆蓋
整合測試 → 模組協作、API 測試
E2E 測試 → 使用者視角、關鍵流程

💡 最佳實踐

  • 只測試關鍵使用者流程
  • 使用 Page Object 模式組織程式碼
  • 避免測試實作細節、保持測試獨立性、設定合理的超時時間

小提醒:E2E 測試雖然強大,但執行時間較長。記得合理安排測試策略,確保關鍵流程都有保護!


上一篇
Day 26 - 效能測試 ⚡
系列文
Laravel Pest TDD 實戰:從零開始的測試驅動開發27
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言