Laravelのテスト自動化をDuskで実装する方法|インストールからCI実行まで
Laravelのテスト自動化は、大きく単体・機能テスト(PHPUnit/Pest)とブラウザE2Eテストの2層に分かれます。前者はコードから直接ロジックやHTTPレスポンスを検証し、後者は実際のブラウザを操作して「ユーザーが画面で行う操作」そのものを自動化します。このブラウザ層を担う公式ツールがLaravel Duskです。本記事は、Duskを使ったブラウザテスト自動化に絞って、全体像・インストール・テストの書き方・待機とアサート・GitHub ActionsでのCI実行・Pest 4との使い分けまでを一気通貫で解説します。単体・機能テストの書き方はPestPHPとは?シンプルで読みやすいPHPテストフレームワークの概要で扱っているため、本記事では重複せずブラウザ操作の自動化に集中します。バージョンはDusk v8.6.0(2026年4月時点、PHP 8.1以上・Laravel 10〜13対応)を前提とします。
まとめ:Laravel Duskによるテスト自動化の要点
- Laravelのテスト自動化は「単体/機能テスト(PHPUnit・Pest)」と「ブラウザE2Eテスト(Dusk)」の2層。Duskは実ブラウザ(Chrome)を操作するE2E担当。
- 導入は
composer require laravel/dusk --devとphp artisan dusk:installの2コマンド。ChromeDriverは自動で用意される。 - 要素の指定はCSSセレクタより dusk属性(テスト側は
@接頭辞)で安定させる。 - 動的ページでは 待機処理(
waitFor/waitForText/whenAvailable)が安定の鍵。 - CIはGitHub ActionsでChromeDriver起動→
php artisan serve→php artisan duskの順に実行する。 - 新規プロジェクトは公式がPest 4のブラウザテストを推奨。既存資産や特定要件がある場合にDuskを選ぶ、という判断軸で使い分ける。
以降で、テスト自動化の全体像におけるDuskの位置づけから、具体的な実装とCI運用までを順に見ていきます。
Laravelのテスト自動化の全体像とDuskの位置づけ
「Laravel テスト自動化」と一口に言っても、検証する対象によって使うツールが変わります。まず全体像を押さえると、Duskをどこで使うべきかが明確になります。Laravelのテストは概ね次の3層に整理できます。
| 層 | 検証対象 | 主なツール | ブラウザ |
|---|---|---|---|
| 単体テスト | クラス・メソッド単体のロジック | PHPUnit / Pest | 不要 |
| 機能テスト | HTTPリクエストとレスポンス・DB状態 | PHPUnit / Pest | 不要 |
| ブラウザE2Eテスト | 画面操作・JavaScript込みの挙動 | Laravel Dusk | 実ブラウザ(Chrome) |
単体・機能テストは、LaravelがカーネルをPHPプロセス内で起動して直接リクエストを流すためブラウザを起動しません。高速で、CIでも軽く回せます。一方、Vue・React・AlpineなどのJavaScriptで動く画面や、フォーム送信後のリダイレクト、モーダル表示といった「実際に描画された結果」は、PHPプロセス内のテストでは検証できません。ここを埋めるのがDuskです。Duskは実際のChromeを立ち上げ、人間と同じようにクリック・入力・待機を行います。
したがって使い分けの判断はシンプルです。ロジックとAPIレスポンスは機能テスト(Pest/PHPUnit)で数多く速く、画面全体の動作確認だけをDuskで少数に絞るのが定石です。E2Eは実行が重く壊れやすいため、全画面をDuskで網羅しようとすると保守コストが跳ね上がります。単体・機能テストの具体的な書き方はPestPHPの概要記事にまとめています。
Laravel Duskとは──実ブラウザを操作するE2Eテストツール
Laravel Duskは、Laravel公式が提供するブラウザ自動化・E2Eテスト用パッケージです。特徴は、テスト実行時にChromeDriver経由で実際のGoogle Chromeを起動し、指定したURLを開いてクリックや入力を自動で行う点にあります。Seleniumのように別途Javaの実行環境や外部サーバーを用意する必要がなく、php artisan dusk:install がChromeDriverのバイナリまで含めて環境を整えてくれるため、LaravelプロジェクトにComposerで追加するだけでブラウザテストを始められます。
Duskで自動化できるのは、たとえばログインフォームに入力して送信し遷移先を確認する、モーダルが表示されるまで待ってボタンを押す、JavaScriptで動的に増える要素の件数を検証する、といった「画面越しのユーザー操作」全般です。テスト失敗時には自動でスクリーンショットを保存するため、CI上で落ちたときの原因調査もしやすくなっています。
Laravel Duskのインストールと初期設定
Composerでの導入とdusk:install
Duskはテストにのみ使うため、開発依存として追加します。追加後に dusk:install を実行すると、tests/Browser ディレクトリ・サンプルテスト・DuskTestCase 基底クラス・OSに合ったChromeDriverが一括で用意されます。
composer require laravel/dusk --dev
php artisan dusk:install
導入後は、テストがアクセスする先を .env の APP_URL に合わせておきます(例: http://127.0.0.1:8000)。Duskはここに書かれたURLを基準に visit の相対パスを解決するため、実際に php artisan serve で起動するアドレスと一致していないと、visit の解決先がずれてテストが失敗します。
テスト専用の環境分離(.env.dusk)とChromeDriver更新
本番・開発用のDBをテストで壊さないよう、Dusk専用の環境ファイルを用意できます。.env.dusk.local のように .env.dusk.{環境名} を置くと、Duskは実行時に既存の .env を退避してこちらを読み込み、終了後に元へ戻します。テスト用DBを分けておくと安全です。
APP_URL=http://127.0.0.1:8000
APP_ENV=dusk
DB_DATABASE=dusk_database
Chrome本体は自動更新で新しくなるため、ChromeDriver側とバージョンがずれると起動に失敗します。ずれた場合は、インストール済みChromeのバージョンを検出して合わせる次のコマンドで解消します。
php artisan dusk:chrome-driver --detect
Duskテストの書き方──基本構造とブラウザ操作
テストクラスの基本構造とデータベース
Duskテストは DuskTestCase を継承し、browse メソッドに渡したクロージャの中で Browser インスタンスを操作します。テストごとにDBを初期化するには DatabaseMigrations または、より高速な DatabaseTruncation トレイトを使います。なおDuskは別プロセスでブラウザを動かすため、SQLiteのインメモリDBは利用できません(プロセス間で共有されないため)。ファイルベースのテスト用DBを使ってください。
<?php
namespace Tests\Browser;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTruncation;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class LoginTest extends DuskTestCase
{
use DatabaseTruncation;
public function test_user_can_login(): void
{
$user = User::factory()->create([
'email' => '[email protected]',
]);
$this->browse(function (Browser $browser) use ($user) {
$browser->visit('/login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertPathIs('/home');
});
}
}
ページ操作の基本メソッド
ブラウザ操作はメソッドチェーンで記述します。よく使うのは遷移の visit/visitRoute、入力の type/select/check、実行の press(ボタン)/clickLink(リンク文字列)です。フォームを埋めて送信し、結果ページを検証する、という一連の流れを自然に書けます。
$browser->visit('/register')
->type('name', 'Taro')
->select('plan', 'pro')
->check('terms')
->press('Sign Up')
->assertPathIs('/dashboard');
dusk属性でセレクタを安定させる
E2Eテストが壊れやすい最大の原因は、CSSクラスやテキストの変更でセレクタが外れることです。Duskは要素に専用の dusk 属性を付け、テスト側から @ 接頭辞で参照する方法を推奨しています。デザイン変更でクラス名が変わってもテストが壊れません。
<button dusk="login-button">Login</button>
$browser->click('@login-button');
待機処理とアサーション──動的ページを安定してテストする
要素の出現を待つ待機メソッド
JavaScriptで後から描画される要素をすぐに操作しようとすると、まだ存在せずテストが失敗します。Duskは指定要素が現れるまで待つ waitFor、テキストが出るまで待つ waitForText、要素が現れてからその中を操作する whenAvailable などを備えます。既定の待機上限は5秒で、第2引数で秒数を変えられます。pause による固定待機は不安定になりやすいため、条件付きの待機を優先します。
$browser->waitFor('@server-list', 3)
->waitForText('Ready')
->whenAvailable('@confirm-modal', function ($modal) {
$modal->assertSee('Are you sure?')->press('OK');
});
結果を検証するアサーション
操作の結果は各種アサーションで確認します。表示文言は assertSee/assertDontSee、URLやパスは assertPathIs/assertRouteIs、ページタイトルは assertTitle、入力値やチェック状態は assertInputValue/assertChecked、ログイン状態は assertAuthenticated で検証できます。
$browser->assertPathIs('/home')
->assertSee('ようこそ')
->assertAuthenticated();
ヘッドレスモードとGitHub ActionsでのCI自動化
ヘッドレスモードの設定
CIサーバーには画面(GUI)がないため、Chromeを画面表示なしで動かすヘッドレスモードで実行します。Duskでは tests/DuskTestCase.php の driver メソッドでChromeの起動オプションを指定し、--headless=new や --disable-gpu、コンテナ環境向けの --no-sandbox を渡します。ローカルでは画面ありで挙動を目視し、CIではヘッドレスにする、といった切り替えが一般的です。
GitHub ActionsでのDuskテスト実行
CIでは「ChromeDriverを対象Chromeに合わせて更新」→「ChromeDriver起動」→「アプリをサーブ」→「Dusk実行」の順に組みます。GitHub Actionsのステップ例は次のとおりです。なおChromeDriverと serve はバックグラウンド起動のため、これらとDusk実行は同一ジョブの連続ステップに置きます(別ジョブに分けるとバックグラウンドプロセスが引き継がれません)。継続的インテグレーション全体の組み方はGitHub Actionsでビルド・自動テストを設定する方法|CI/CDワークフローの作り方で解説しています。
- name: Update Chrome Driver
run: php artisan dusk:chrome-driver --detect
- name: Start Chrome Driver
run: ./vendor/laravel/dusk/bin/chromedriver-linux --port=9515 &
- name: Run Laravel Server
run: php artisan serve --no-reload &
- name: Run Dusk Tests
env:
APP_URL: "http://127.0.0.1:8000"
run: php artisan dusk
テストの実行・デバッグとスクリーンショット
テストは php artisan dusk で実行します。失敗したテストだけを再実行するなら php artisan dusk:fails、特定グループに絞るなら --group オプションを使います。php artisan test がPHPUnit/Pestのユニット・機能テストを回すのに対し、Duskは専用の dusk コマンドで動く点に注意してください。
php artisan dusk
php artisan dusk:fails
php artisan dusk --group=checkout
Duskはテスト失敗時に tests/Browser/screenshots へ自動でスクリーンショットを保存します。任意のタイミングで撮る screenshot、ブラウザのコンソールログを残す storeConsoleLog、HTMLソースを保存する storeSource を使うと、CI上でしか再現しない不具合の調査が容易になります。
$browser->screenshot('checkout-step1');
$browser->storeConsoleLog('checkout-console');
DuskとPest 4ブラウザテストの選び方
2026年時点で、Laravel公式ドキュメントは新規プロジェクトのブラウザテストにDuskではなくPest 4を推奨しています。Pest 4はブラウザテスト機能を内蔵し、Duskより高速でAPIも簡潔だからです。とはいえ既存のDuskテストが直ちに動かなくなるわけではなく、Duskは引き続き保守されています。判断軸を整理します。
| 観点 | Laravel Dusk | Pest 4 ブラウザテスト |
|---|---|---|
| 公式の推奨 | (既存資産では継続が現実的) | 新規プロジェクトに推奨 |
| 記法 | Browserメソッドチェーン | Pestのit/expect構文 |
| ドライバ | ChromeDriver(Chrome) | Playwright系(複数ブラウザ) |
| 導入コスト | dusk:installで完結 | Pest導入が前提 |
結論として、これから書き始めるならPest 4のブラウザテストを第一候補にすべきです。一方で、すでにDuskのテスト資産が積み上がっているプロジェクトや、dusk:install だけで完結する手軽さを重視する場合は、Duskを継続する判断も十分に合理的です。無理に全面移行するより、既存はDuskで維持し新規はPest 4で書く、という併存も現実的な選択肢です。Pest 4のブラウザテストの詳細はPest v4とは何か?概要と特徴を徹底解説【新機能・進化ポイントまで】を参照してください。
よくある質問
Laravel Duskとは何ですか?
実際のGoogle Chromeを操作して画面越しのユーザー操作を自動化する、Laravel公式のブラウザE2Eテスト用パッケージです。php artisan dusk:install でChromeDriverまで含めて環境が整い、クリック・入力・待機・アサートをコードで記述してブラウザテストを自動実行できます。
DuskとPHPUnit/Pestのテストは何が違いますか?
PHPUnitやPestの単体・機能テストはブラウザを起動せず、PHPプロセス内でロジックやHTTPレスポンスを高速に検証します。Duskは実ブラウザを起動し、JavaScript込みの画面挙動を検証するE2E層を担います。速いロジック検証はPest/PHPUnit、画面全体の動作確認だけをDuskに、と使い分けるのが基本です。
DuskとPest 4のブラウザテストはどちらを使うべきですか?
新規プロジェクトは、公式が推奨するPest 4のブラウザテストが第一候補です。すでにDuskのテスト資産があるプロジェクトや、追加のPest導入なしに始めたい場合はDuskの継続が合理的です。既存はDusk・新規はPest 4という併存も可能です。
ヘッドレスモードでテストが動かないときは?
まずChromeとChromeDriverのバージョンずれを php artisan dusk:chrome-driver --detect で解消します。コンテナやCIでは --no-sandbox/--disable-gpu オプションの付与、APP_URL と実際のサーブ先の一致、ChromeDriverの起動忘れがないかを確認してください。
日本語や多言語ページのテストはできますか?
可能です。assertSee に日本語文言をそのまま渡して検証でき、ロケールを切り替えた画面表示の確認もできます。言語切り替え後は要素が再描画されるため、waitForText で表示を待ってからアサートすると安定します。