既存のLaravelプロジェクトにおいて、アプリケーションの品質向上とデプロイ後の不具合検知を目的として、E2Eテストツールの導入を検討しました。

今回は、Laravel標準の「Dusk」、それをモダンに記述できる「Pest + Dusk」、そして別言語のエコシステムである「Playwright」の3つを比較調査し、最終的な選定プロセスをまとめます。

1. E2Eテスト導入の背景

これまで手作業中心のテストを実施していましたが、以下の課題を解決するためにブラウザレベルのテストが必要と判断しました。

  • JavaScript(Vue.js)が絡むインタラクティブなUIの動作確認を自動化したい
  • 各環境においてデプロイ後に毎回実施している確認作業を自動化して効率化したい
  • 本番リリース直後に「ログインできない」「主要なボタンが機能しない」といった重大な事故を未然に防ぎたい

2. 候補1:Laravel Dusk

まずはLaravel公式のブラウザテストツールである Laravel Dusk を調査しました。標準的なPHPUnitベースの記述スタイルです。

実装例

namespace Tests\Browser\Login;

use Laravel\Dusk\Browser;
use Tests\DuskTestCase;

class UserLoginTestClassBased extends DuskTestCase
{
    public function test_存在しないメールアドレスでログインするとエラーメッセージが表示される(): void
    {
        $this->browse(function (Browser $browser) {
            $browser->visit('/login')
                ->type('#email', 'notexist-dusk-test-' . time() . '@example.com')
                ->type('#password', 'wrongpassword')
                ->press('ログイン')
                ->waitFor('.input-text__error-message')
                ->assertVisible('.input-text__error-message');
        });
    }
}

調査結果

  • メリット: Laravel公式ツールのため、ドキュメントが豊富でDBや認証との連携がスムーズ。Laravel開発者の学習コストが低い。
  • 課題点: クラスベースの記述が必要で、テストコードが冗長になりがち。また、実行速度がWebDriverに依存するため、大規模なテストスイートでは時間がかかる懸念がある。

3. 候補2:Dusk + Pest

次に、Duskをより簡潔に記述できる Pest との組み合わせを調査しました。

実装例

Pestを使用すると、クラスの宣言やアノテーションが不要になり、直感的な記述が可能になります。

use Laravel\Dusk\Browser;

it('存在しないメールアドレスでログインするとエラーメッセージが表示される', function () {
    $this->browse(function (Browser $browser) {
        $browser->visit('/login')
            ->type('#email', 'notexist-dusk-test-' . time() . '@example.com')
            ->type('#password', 'wrongpassword')
            ->press('ログイン')
            ->waitFor('.input-text__error-message')
            ->assertVisible('.input-text__error-message');
    });
});

調査結果

  • メリット: 記述がシンプルになり、テストの意図(仕様)が読み取りやすくなる。
  • 課題点: 書き味は向上するが、ブラウザ操作のエンジン自体はDuskのままであるため、実行速度や「要素の待機(Wait)」に関する不安定さは解消されない。

4. 候補3:Playwright

最後に、Microsoftが開発している Playwright を調査しました。昨今のE2Eテストの定番となっています。

実装例

Playwrightは外部からブラウザを操作するため、実際のURLを指定して操作フローを記述します。

import { test, expect } from '@playwright/test';
import { loginAsUser, TEST_USER } from '../../helpers/auth';

test.describe('ログイン画面 - ログイン成功', () => {
  test('4.1 ログイン成功 → /xxxxxx/xxxxxxxxxx/start へリダイレクト', async ({ page }) => {
    await page.goto('/login');
    await page.locator('#email').fill(TEST_USER.email);
    await page.locator('#password').fill(TEST_USER.password);
    await page.locator('button[type="submit"]').click();
    await expect(page).toHaveURL(/\/(xxxxxxxxxx|yyyyyyyyy)/, { timeout: 15000 });
  });
});

調査結果

  • メリット
    • Auto-waiting(自動待機): 要素が操作可能になるまで自動で待機するため、非同期処理が多い画面でもテストが壊れにくい。
    • Trace Viewer: テスト失敗時の状況をタイムライン形式で後から追跡できる。
      • `npx playwright show-report` を実行し、実行ログやキャプチャなどを確認可能。
    • 実行速度: 独自プロトコルによりDuskよりも高速に動作し、並列実行も標準でサポートされている。
      • コア数に合わせて並列数をスケールすることが可能で、18件のテストを並列数1と並列数5で実行したところ以下のようにテスト実行時間を大幅に削減可能だった。
        • 並列数1:35.8s
        • 並列数5:12.2s
  • 課題点: TypeScriptで記述するためPHPのみの開発者には学習コストがかかる。(Playwright PHPというPHPで書けるライブラリもあるが、PHPで書けること以外特にメリットがないように思う)

5. 比較まとめ

比較項目 Dusk Dusk + Pest Playwright
記法 PHP・クラスベース (冗長) PHP・関数ベース (簡潔) JavaScript (モダン)
Laravel連携 ◎ (DB/Auth操作可能) ◎ (DB/Auth操作可能) △ (外部からのアクセスのみ)
実行速度 ◎ (高速)
デバッグ体験 ○ (スクショ) ○ (スクショ) ◎ (Trace Viewer)
安定性 △ (手動待機) △ (手動待機) ◎ (自動待機)

6. 結論

実際にそれぞれ試してみた結果、「各環境においてデプロイ後に毎回実施している確認作業を自動化して効率化したい」という目的に合っているの
ものとして、Playwrightを採用することにしました。

今回の目的にはテスト自体にLaravel連携を必要としないためDuskでしかできないことは無いです。また、DuskのテストコードはLaravelアプリの一部として動くため、テストが実行されるCI上にPHP + Composer + Laravel の実行環境が必要となり、加えてブラウザ操作のために ChromeDriver も必要となります。CIに組み込むには重いし管理コストが高いですね。
PlaywrightはNode.js(とchromium)のみで実行可能なのでCIに組み込みやすい、並列処理で速度を上げられるという点が採用となったポイントです。

最近はテストコードはAIが書いてくれるため、TypescriptかPHPかという言語の差は大きな懸念事項とはならないですね。