Laravel 6.x Laravel Dusk

イントロダクション

Laravel Dusk(ダースク:夕暮れ)は、利用が簡単なブラウザの自動操作/テストAPIを提供します。デフォルトのDuskは皆さんのマシンへ、JDKやSeleniumのインストールを求めません。代わりにDuskはスタンドアローンのChromeDriverを使用します。しかし、好みのSeleniumコンパチドライバも自由に使用することもできます。

インストール

使用を開始するには、laravel/duskコンポーサ依存パッケージをプロジェクトへ追加します。

composer require --dev laravel/dusk

Note: 本番環境にDuskをインストールしてはいけません。インストールすると、アプリケーションに対する未認証でのアクセスを許すようになります。

Duskパッケージをインストールし終えたら、dusk:install Artisanコマンドを実行します。

php artisan dusk:install

testディレクトリ中に、サンプルテストを含んだBrowserディレクトリが作成されます。次に、.envファイルでAPP_URL環境変数を指定します。この値は、ブラウザからアクセスするアプリケーションで使用するURLと一致させます。

テストを実行するには、dusk Artisanコマンドを使います。duskコマンドには、phpunitコマンドが受け付ける引数をすべて指定できます。

php artisan dusk

duskコマンドで最後に実行したテストが失敗した場合、dusk:failsコマンドを使用し、失敗したテストを再実行することにより、時間を節約できます。

php artisan dusk:fails

ChromeDriverインストールの管理

Laravel Duskにデフォルトで含まれるChromeDriverとは別のバージョンをインストールしたい場合は、dusk:chrome-driverコマンドが使用できます。

# OSに合う、最新バージョンのChromeDriverのインストール
php artisan dusk:chrome-driver

# OSに合う、指定バージョンのChromeDriverのインストール
php artisan dusk:chrome-driver 74

# 全OSをサポートしている、指定バージョンのChromeDriverのインストール
php artisan dusk:chrome-driver --all

Note: Dusk実行には、実行可能なchromedriverバイナリが必要です。Dusk実行時に問題がある場合は、このバイナリを実行可能に確実にするために、chmod -R 0755 vendor/laravel/dusk/binコマンドを実行してみてください。

他ブラウザの使用

デフォルトのDuskは、Google ChromeとスタンドアローンのChromeDriverをブラウザテスト実行に使用します。しかし、自身のSeleniumサーバを起動し、希望するブラウザに対しテストを実行することもできます。

開始するには、アプリケーションのベースDuskテストケースである、tests/DuskTestCase.phpファイルを開きます。このファイルの中の、startChromeDriverメソッド呼び出しを削除してください。これにより、ChromeDriverの自動起動を停止します。

/**
 * Duskテスト実行準備
 *
 * @beforeClass
 * @return void
 */
public static function prepare()
{
    // static::startChromeDriver();
}

次に、皆さんが選んだURLとポートへ接続するために、driverメソッドを変更します。WebDriverに渡すべき、"desired capabilities"を更新することもできます。

/**
 * RemoteWebDriverインスタンスの生成
 *
 * @return \Facebook\WebDriver\Remote\RemoteWebDriver
 */
protected function driver()
{
    return RemoteWebDriver::create(
        'http://localhost:4444/wd/hub', DesiredCapabilities::phantomjs()
    );
}

利用の開始

テストの生成

Duskのテストを生成するには、dusk:make Artisanコマンドを使います。生成されたテストは、tests/Browserディレクトリへ設置されます。

php artisan dusk:make LoginTest

テストの実行

ブラウザテストを実行するには、dusk Artisanコマンドを使用します。

php artisan dusk

duskコマンドで最後に実行したテストが失敗した場合、dusk:failsコマンドを使用し、失敗したテストを再実行することにより、時間を節約できます。

php artisan dusk:fails

PHPUnitテストランナが通常受け付ける引数は、duskコマンドでも指定できます。たとえば、指定したグループのテストのみを実行するなどです。

php artisan dusk --group=foo

ChromeDriverの手動起動

デフォルトのDuskは、ChromeDriverを自動的に起動しようとします。特定のシステムで自動起動しない場合は、duskコマンドを実行する前に手動でChromeDriverを起動することもできます。ChromeDriverを手動起動する場合は、tests/DuskTestCase.phpファイルの以下の行をコメントアウトしてください。

/**
 * Duskテスト実行準備
 *
 * @beforeClass
 * @return void
 */
public static function prepare()
{
    // static::startChromeDriver();
}

また、ChromeDriverを9515以外のポートで起動した場合、同じクラスのdriverメソッドを変更する必要があります。

/**
 * RemoteWebDriverインスタンスの生成
 *
 * @return \Facebook\WebDriver\Remote\RemoteWebDriver
 */
protected function driver()
{
    return RemoteWebDriver::create(
        'http://localhost:9515', DesiredCapabilities::chrome()
    );
}

環境の処理

テスト実行時に独自の環境ファイルを強制的に使用させるには、プロジェクトのルートに.env.dusk.{environment}ファイルを作成します。たとえば、local環境からduskコマンドを起動する場合は、.env.dusk.localファイルを作成します。

テストを実行すると、Duskは.envファイルをバックアップし、皆さんのDusk環境を.envへリネームします。テストが完了したら、.envファイルをリストアします。

ブラウザの生成

手始めに、アプリケーションへログインできることを確認するテストを書いてみましょう。テストを生成したら、ログインページへ移動し、認証情報を入力し、"Login"ボタンをクリックするように変更します。ブラウザインスタンスを生成するには、browserメソッドを呼び出します。

<?php

namespace Tests\Browser;

use App\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;

class ExampleTest extends DuskTestCase
{
    use DatabaseMigrations;

    /**
     * 基本的なブラウザテスト例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $user = factory(User::class)->create([
            'email' => 'taylor@laravel.com',
        ]);

        $this->browse(function ($browser) use ($user) {
            $browser->visit('/login')
                    ->type('email', $user->email)
                    ->type('password', 'password')
                    ->press('Login')
                    ->assertPathIs('/home');
        });
    }
}

上記の例のように、browseメソッドはコールバックを引数に受けます。 Duskによりブラウザインスタンスは自動的にこのコールバックに渡され、このオブジェクトで、アプリケーションに対する操作やアサートを行います。

複数ブラウザの生成

テストを行うために複数のブラウザが必要なこともあります。たとえば、Webソケットを使用するチャットスクリーンをテストするためには、複数のブラウザが必要でしょう。複数ブラウザを生成するには、browseメソッドに指定するコールバックの引数で、一つ以上のブラウザを指定します。

$this->browse(function ($first, $second) {
    $first->loginAs(User::find(1))
          ->visit('/home')
          ->waitForText('Message');

    $second->loginAs(User::find(2))
           ->visit('/home')
           ->waitForText('Message')
           ->type('message', 'Hey Taylor')
           ->press('Send');

    $first->waitForText('Hey Taylor')
          ->assertSee('Jeffrey Way');
});

ブラウザウィンドウのリサイズ

ブラウザウインドウのサイズを調整するため、resizeメソッドを使用できます。

$browser->resize(1920, 1080);

ブラウザウィンドウを最大化するには、maximizeメソッドを使います。

$browser->maximize();

fitContentメソッドは、コンテンツに合わせてブラウザウィンドウのサイズをリサイズします。

$browser->fitContent();

テスト失敗時にDuskはスクリーンショットを取るために、以前のコンテンツに合うようブラウザを自動的にリサイズします。この機能を無効にするには、テストの中でdisableFitOnFailureメソッドを呼び出してください。

$browser->disableFitOnFailure();

ブラウザマクロ

さまざまなテストで再利用可能な、カスタムブラウザメソッドを定義したい場合は、Browserクラスのmacroメソッドを使用してください。

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Browser;

class DuskServiceProvider extends ServiceProvider
{
    /**
     * Duskのブラウザマクロの登録
     *
     * @return void
     */
    public function boot()
    {
        Browser::macro('scrollToElement', function ($element = null) {
            $this->script("$('html, body').animate({ scrollTop: $('$element').offset().top }, 0);");

            return $this;
        });
    }
}

macro関数の第1引数は名前で、第2引数はクロージャです。このクロージャは、Browser実装上でメソッドとしてマクロが呼び出された時に、実行されます。

$this->browse(function ($browser) use ($user) {
    $browser->visit('/pay')
            ->scrollToElement('#credit-card-details')
            ->assertSee('Enter Credit Card Details');
});

認証

認証が必要なページのテストはよくあります。毎回テストのたびにログインスクリーンを操作しなくても済むように、DuskのloginAsメソッドを使ってください。loginAsメソッドはユーザーIDかユーザーモデルインスタンスを引数に取ります。

$this->browse(function ($first, $second) {
    $first->loginAs(User::find(1))
          ->visit('/home');
});

Note: loginAsメソッドを使用後、そのファイルに含まれるすべてのテストに対し、ユーザーセッションは保持されます。

データベースマイグレーション

上記の認証サンプルのように、マイグレーションをテストする必要がある場合は、RefreshDatabaseトレイトを使用してはいけません。RefreshDatabaseトレイトはHTTPリクエストに対し適用されない、データベーストランザクションに活用します。代わりに、DatabaseMigrationsトレイトを使用してください。

<?php

namespace Tests\Browser;

use App\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Chrome;
use Tests\DuskTestCase;

class ExampleTest extends DuskTestCase
{
    use DatabaseMigrations;
}

要素の操作

Duskセレクタ

要素を操作するために、最適なCSSセレクタを選択するのは、Duskテストで一番難しい部分です。フロントエンドは繰り返し変更され、失敗するようになったテストを修正するため、CSSセレクタを何度も調整しました。

// HTML

<button>Login</button>

// テスト

$browser->click('.login-page .container div > button');

Duskセレクタにより、CSSセレクタを記憶せず効率的にテストを書くことへ集中できるようになります。セレクタを定義するには、HTML要素にdusk属性を追加します。それから、Duskテスト中の要素を操作するために、セレクタの先頭に@を付けてください。

// HTML

<button dusk="login-button">Login</button>

// テスト

$browser->click('@login-button');

リンクのクリック

リンクをクリックするには、ブラウザインスタンスのclickLinkメソッドを使います。clickLinkメソッドは指定した表示テキストのリンクをクリックします。

$browser->clickLink($linkText);

Note: このメソッドはjQueryを操作します。jQueryがそのページで使用不可能な場合、Duskは自動的にそのページへ挿入し、テストの中に使用します。

テキスト、値、属性

値の取得/設定

Duskは現在表示されているテキスト、値、ページ要素の属性を操作する、数多くのメソッドを提供します。たとえば、指定したセレクタに一致する要素の「値(value)」を取得するには、valueメソッドを使用します。

// 値の取得
$value = $browser->value('selector');

// 値の設定
$browser->value('selector', 'value');

テキストの取得

textメソッドは、指定したセレクタに一致する要素の表示テキストを取得します。

$text = $browser->text('selector');

属性の取得

最後のattributeメソッドは、セレクタに一致する要素の属性を取得します。

$attribute = $browser->attribute('selector', 'value');

フォームの使用

値のタイプ

Duskはフォームと入力要素を操作する、さまざまなメソッドを提供しています。最初に、入力フィールドへテキストをタイプする例を見てみましょう。

$browser->type('email', 'taylor@laravel.com');

必要であれば受け付けますが、typeメソッドにはCSSセレクタを渡す必要がないことに注意してください。CSSセレクタが指定されない場合、Duskはname属性に指定された入力フィールドを探します。最終的に、Duskは指定されたname属性を持つtextareaを見つけようとします。

コンテンツをクリアせずに、フィールドへテキストを追加するには、appendメソッドを使用します。

$browser->type('tags', 'foo')
        ->append('tags', ', bar, baz');

入力値をクリアするには、clearメソッドを使用します。

$browser->clear('email');

ドロップダウン

ドロップダウンの選択ボックスから値を選ぶには、selectメソッドを使います。typeメソッドと同様に、selectメソッドも完全なCSSセレクタは必要ありません。selectメソッドに引数を指定するとき、表示テキストの代わりに、オプション値を渡します。

$browser->select('size', 'Large');

第2引数を省略した場合、ランダムにオプションを選択します。

$browser->select('size');

チェックボックス

チェックボックスを「チェック(check)」するには、checkメソッドを使います。他の関連する多くのメソッドと同様に、完全なCSSセレクタは必要ありません。完全に一致するセレクタが見つからないと、Duskはname属性に一致するチェックボックスを探します。

$browser->check('terms');

$browser->uncheck('terms');

ラジオボタン

ラジオボタンのオプションを「選択」するには、radioメソッドを使用します。他の関連する多くのメソッドと同様に、完全なセレクタは必要ありません。完全に一致するセレクタが見つからない場合、Duskはnamevalue属性に一致するラジオボタンを探します。

$browser->radio('version', 'php7');

添付ファイル

attachメソッドはfile入力要素で、ファイルを指定するために使用します。他の関連する入力メソッドと同様に、完全なCSSセレクタは必要ありません。完全なセレクタが見つからなければ、Duskはname属性と一致するファイル入力を探します。

$browser->attach('photo', __DIR__.'/photos/me.png');

Note: attach関数を利用するには、サーバへZip PHP拡張をインストールし、有効にする必要があります。

キーワードの使用

keysメソッドは、typeメソッドによる、指定した要素に対する通常の入力よりも、複雑な入力を提供します。たとえば、モデファイヤキーを押しながら、値を入力するなどです。以下の例では、指定したセレクタに一致する要素へ、taylorを「シフト(shift)」キーを押しながら入力します。Taylorをタイプし終えると、otwellがモデファイヤキーを押さずにタイプされます。

$browser->keys('selector', ['{shift}', 'taylor'], 'otwell');

アプリケーションを構成する主要なCSSセレクタへ「ホットキー」を送ることもできます。

$browser->keys('.app', ['{command}', 'j']);

Tip!! モデファイヤキーは{}文字で囲み、Facebook\WebDriver\WebDriverKeysクラスで定義されている定数を指定します。GitHubで確認できます。

マウスの使用

要素のクリック

指定したセレクタに一致する要素を「クリック」するには、clickメソッドを使います。

$browser->click('.selector');

マウスオーバー

指定したセレクタに一致する要素を「マウスオーバー」したい場合は、mouseoverメソッドを使います。

$browser->mouseover('.selector');

ドラッグ&ドロップ

dragメソッドは指定したセレクタに一致する要素をドラッグし、もう一つの要素へドロップします。

$browser->drag('.from-selector', '.to-selector');

もしくは、特定の方向へ要素をドラッグすることもできます。

$browser->dragLeft('.selector', 10);
$browser->dragRight('.selector', 10);
$browser->dragUp('.selector', 10);
$browser->dragDown('.selector', 10);

JavaScriptダイアログ

DuskはJavaScriptダイアログを操作する、さまざまなメソッドを提供しています。

// ダイアログが表示されるのを待つ
$browser->waitForDialog($seconds = null);

// ダイアログが表示され、メッセージが指定した値と一致することを宣言
$browser->assertDialogOpened('value');

// 開いているJavaScript入力(prompt)ダイアログに、指定値をタイプ
$browser->typeInDialog('Hello World');

開いているJavaScriptダイアログをOKボタンのクリックで閉じるには:

$browser->acceptDialog();

開いているJavaScriptダイアログをキャンセルボタンのクリックで閉じるには(確認ダイアログのみ):

$browser->dismissDialog();

セレクタの範囲指定

特定のセレクタの中の全操作を範囲指定しつつ、多くの操作を行いたいこともあります。たとえば、いくつかのテーブル中にあるテキストが存在していることをアサートし、それからテーブル中のボタンをクリックしたい場合です。withメソッドで行なえます。withメソッドのコールバック中で行われた操作は全部、オリジナルのセレクタに対し限定されます。

$browser->with('.table', function ($table) {
    $table->assertSee('Hello World')
          ->clickLink('Delete');
});

要素の待機

広範囲に渡りJavaScriptを使用しているアプリケーションのテストでは、テストを進める前に特定の要素やデータが利用可能になるまで、「待つ(wait)」必要がしばしば起きます。Duskではこれも簡単に行えます。数多くのメソッドを使い、ページで要素が見えるようになるまで、もしくはJavaScriptの評価がtrueになるまで待機できます。

待機

指定したミリ秒の間、テストをポーズしたい場合は、pauseメソッドを使用します。

$browser->pause(1000);

セレクタの待機

waitForメソッドはテストの実行を指定したCSSセレクタがページに表示されるまで中断します。例外が投げられるまで、デフォルトで最大5秒間テストを中断します。必要であれば、カスタムタイムアウトを秒でメソッドの第2引数として指定できます。

// セレクタを最大5秒間待つ
$browser->waitFor('.selector');

// セレクタを最大1秒待つ
$browser->waitFor('.selector', 1);

指定したセレクタがページから消えるまで待つこともできます。

$browser->waitUntilMissing('.selector');

$browser->waitUntilMissing('.selector', 1);

利用可能時限定のセレクタ

指定したセレクタを待ち、それからそのセレクタに一致する要素を操作したい場合もよくあります。たとえば、モーダルウィンドウが現れるまで待ち、それからそのモーダルウィンドウ上の"OK"ボタンを押したい場合です。このケースではwhenAvailableメソッドを使用します。指定したコールバック内で行われた要素操作はすべて、オリジナルのセレクタに対して限定されます。

$browser->whenAvailable('.modal', function ($modal) {
    $modal->assertSee('Hello World')
          ->press('OK');
});

テキストの待機

指定したテキストがページに表示されるまで待ちたい場合は、waitForTextメソッドを使います。

// テキストを最大5秒間待つ
$browser->waitForText('Hello World');

// テキストを最大1秒待つ
$browser->waitForText('Hello World', 1);

ページに表示されている指定したテキストが削除されるまで待ちたい場合は、waitUntilMissingTextメソッドを使います。

// テキストが削除されるまで最大5秒間待つ
$browser->waitUntilMissingText('Hello World');

// テキストが削除されるまで最大1秒間待つ
$browser->waitUntilMissingText('Hello World', 1);

リンクの待機

ページに指定したリンクテキストが表示されるまで待つ場合は、waitForLinkメソッドを使います。

// リンクを最大5秒間待つ
$browser->waitForLink('Create');

// リンクを最大1秒間待つ
$browser->waitForLink('Create', 1);

ページロケーションの待機

$browser->assertPathIs('/home')のようなパスをアサートするときに、window.location.pathnameが非同期更新中の場合、アサートは失敗するでしょう。指定値のロケーションを待機するために、waitForLocationメソッドを使ってください。

$browser->waitForLocation('/secret');

名前付きルートのロケーションを待機することも可能です。

$browser->waitForRoute($routeName, $parameters);

ページリロードの待機

ページのリロード後にアサートする必要がある場合は、waitForReloadメソッドを使ってください。

$browser->click('.some-action')
        ->waitForReload()
        ->assertSee('something');

JavaScriptの評価の待機

指定したJavaScript式の評価がtrueになるまで、テストの実行を中断したい場合もときどきあります。waitUntilメソッドで簡単に行えます。このメソッドに式を渡す時に、returnキーワードや最後のセミコロンを含める必要はありません。

// 式がtrueになるまで最大5秒間待つ
$browser->waitUntil('App.dataLoaded');

$browser->waitUntil('App.data.servers.length > 0');

// 式がtrueになるまで最大1秒間待つ
$browser->waitUntil('App.data.servers.length > 0', 1);

Vue式でwaitする

以下のメソッドで、特定のVueコンポーネント属性が、指定値になるまで待つことができます。

// 指定したコンポーネント属性が、指定値を含むまで待つ
$browser->waitUntilVue('user.name', 'Taylor', '@user');

// 指定したコンポーネント属性が、指定値を含まなくなるまで待つ
$browser->waitUntilVueIsNot('user.name', null, '@user');

コールバックによる待機

Duskにある数多くの「待機」メソッドは、waitUsingメソッドを使用しています。このメソッドを直接利用し、コールバックがtrueを返すまで待機できます。waitUsingメソッドは最長待ち秒数とクロージャを評価する間隔秒数、クロージャを引数に取ります。オプションとして、失敗時のメッセージを引数に取ります。

$browser->waitUsing(10, 1, function () use ($something) {
    return $something->isReady();
}, "Something wasn't ready in time.");

Vueアサーションの作成

Duskでは、Vueコンポーネントデータの状態をアサートすることもできます。たとえば、アプリケーションに以下のVueコンポーネントが含まれていると想像してください。

// HTML

<profile dusk="profile-component"></profile>

// コンポーネント定義

Vue.component('profile', {
    template: '<div>{{ user.name }}</div>',

    data: function () {
        return {
            user: {
              name: 'Taylor'
            }
        };
    }
});

Vueコンポーネントの状態を以下のようにアサートできます。

/**
 * 基本的なVueのテスト
 *
 * @return void
 */
public function testVue()
{
    $this->browse(function (Browser $browser) {
        $browser->visit('/')
                ->assertVue('user.name', 'Taylor', '@profile-component');
    });
}

使用可能なアサート

Duskはアプリケーションに対する数多くのアサートを提供しています。使用できるアサートを以下のリストにまとめます。

assertTitle

ページタイトルが指定した文字列と一致することを宣言します。

$browser->assertTitle($title);

assertTitleContains

ページタイトルに、指定したテキストが含まれていることを宣言します。

$browser->assertTitleContains($title);

assertUrlIs

クエリ文字列を除いた、現在のURLが指定した文字列と一致するのを宣言します。

$browser->assertUrlIs($url);

assertSchemeIs

現在のURLスキームが、指定したスキームと一致することを宣言します。

$browser->assertSchemeIs($scheme);

assertSchemeIsNot

現在のURLスキームが、指定したスキームと一致しないことを宣言します。

$browser->assertSchemeIsNot($scheme);

assertHostIs

現在のURLのホストが、指定したホストと一致することを宣言します。

$browser->assertHostIs($host);

assertHostIsNot

現在のURLのホストが、指定したホストと一致しないことを宣言します。

$browser->assertHostIsNot($host);

assertPortIs

現在のURLポートが、指定したポートと一致することを宣言します。

$browser->assertPortIs($port);

assertPortIsNot

現在のURLポートが、指定したポートと一致しないことを宣言します。

$browser->assertPortIsNot($port);

assertPathBeginsWith

現在のURLパスが指定したパスで始まることを宣言します。

$browser->assertPathBeginsWith($path);

assertPathIs

現在のパスが指定したパスであることを宣言します。

$browser->assertPathIs('/home');

assertPathIsNot

現在のパスが指定したパスではないことを宣言します。

$browser->assertPathIsNot('/home');

assertRouteIs

現在のURLが指定した名前付きルートのURLと一致することを宣言します。

$browser->assertRouteIs($name, $parameters);

assertQueryStringHas

指定したクエリ文字列パラメータが存在していることを宣言します。

$browser->assertQueryStringHas($name);

指定したクエリ文字列パラメータが存在し、指定値を持っていることを宣言します。

$browser->assertQueryStringHas($name, $value);

assertQueryStringMissing

指定した文字列パラメータが存在しないことを宣言します。

$browser->assertQueryStringMissing($name);

assertFragmentIs

現在のフラグメントが、指定したフラグメントと一致することを宣言します。

$browser->assertFragmentIs('anchor');

assertFragmentBeginsWith

現在のフラグメントが、指定したフラグメントで始まることを宣言します。

$browser->assertFragmentBeginsWith('anchor');

assertFragmentIsNot

現在のフラグメントが、指定したフラグメントと一致しないことを宣言します。

$browser->assertFragmentIsNot('anchor');

assertHasCookie

指定したクッキーが存在していることを宣言します。

$browser->assertHasCookie($name);

assertCookieMissing

指定したクッキーが存在していないことを宣言します。

$browser->assertCookieMissing($name);

assertCookieValue

クッキーが指定値を持っていることを宣言します。

$browser->assertCookieValue($name, $value);

assertPlainCookieValue

暗号化されていないクッキーが、指定値を持っていることを宣言します。

$browser->assertPlainCookieValue($name, $value);

assertSee

指定したテキストが、ページ上に存在することを宣言します。

$browser->assertSee($text);

assertDontSee

指定したテキストが、ページ上に存在しないことを宣言します。

$browser->assertDontSee($text);

assertSeeIn

指定したテキストが、セレクタに含まれていることを宣言します。

$browser->assertSeeIn($selector, $text);

assertDontSeeIn

指定したテキストが、セレクタに含まれていないことを宣言します。

$browser->assertDontSeeIn($selector, $text);

assertSourceHas

指定したソースコードが、ページ上に存在していることを宣言します。

$browser->assertSourceHas($code);

assertSourceMissing

指定したソースコードが、ページ上に存在していないことを宣言します。

$browser->assertSourceMissing($code);

assertSeeLink

指定したリンクが、ページ上に存在していることを宣言します。

$browser->assertSeeLink($linkText);

assertDontSeeLink

指定したリンクが、ページ上に存在していないことを宣言します。

$browser->assertDontSeeLink($linkText);

assertInputValue

指定した入力フィールドが、指定値を持っていることを宣言します。

$browser->assertInputValue($field, $value);

assertInputValueIsNot

指定した入力フィールドが、指定値を持っていないことを宣言します。

$browser->assertInputValueIsNot($field, $value);

assertChecked

指定したチェックボックスが、チェック済みであることを宣言します。

$browser->assertChecked($field);

assertNotChecked

指定したチェックボックスが、チェックされていないことを宣言します。

$browser->assertNotChecked($field);

assertRadioSelected

指定したラジオフィールドが選択されていることを宣言します。

$browser->assertRadioSelected($field, $value);

assertRadioNotSelected

指定したラジオフィールドが選択されていないことを宣言します。

$browser->assertRadioNotSelected($field, $value);

assertSelected

指定したドロップダウンで指定値が選択されていることを宣言します。

$browser->assertSelected($field, $value);

assertNotSelected

指定したドロップダウンで指定値が選択されていないことを宣言します。

$browser->assertNotSelected($field, $value);

assertSelectHasOptions

指定した配列値が選択可能であることを宣言します。

$browser->assertSelectHasOptions($field, $values);

assertSelectMissingOptions

指定した配列値が選択不可であることを宣言します。

$browser->assertSelectMissingOptions($field, $values);

assertSelectHasOption

指定したフィールドで、指定した値が選択可能であることを宣言します。

$browser->assertSelectHasOption($field, $value);

assertValue

指定したセレクタに一致する要素が、指定値であることを宣言します。

$browser->assertValue($selector, $value);

assertVisible

指定したセレクタに一致する要素が、ビジブルであることを宣言します。

$browser->assertVisible($selector);

assertPresent

指定したセレクタに一致する要素が、存在することを宣言します。

$browser->assertPresent($selector);

assertMissing

指定したセレクタに一致する要素が、ビジブルでないことを宣言します。

$browser->assertMissing($selector);

assertDialogOpened

指定したメッセージを持つ、JavaScriptダイアログが開かれていることを宣言します。

$browser->assertDialogOpened($message);

assertEnabled

指定したフィールドが、enabledであることを宣言します。

$browser->assertEnabled($field);

assertDisabled

指定したフィールドが、disabledであることを宣言します。

$browser->assertDisabled($field);

assertButtonEnabled

指定したボタンが、enabledであることを宣言します。

$browser->assertButtonEnabled($button);

assertButtonDisabled

指定したボタンが、disabledであることを宣言します。

$browser->assertButtonDisabled($button);

assertFocused

指定したフィールドに、フォーカスがあることを宣言します。

$browser->assertFocused($field);

assertNotFocused

指定したフィールドから、フォーカスが外れていることを宣言します。

$browser->assertNotFocused($field);

assertVue

指定したVueコンポーネントのデータプロパティが、指定値と一致することを宣言します。

$browser->assertVue($property, $value, $componentSelector = null);

assertVueIsNot

指定したVueコンポーネントのデータプロパティが、指定値と一致しないことを宣言します。

$browser->assertVueIsNot($property, $value, $componentSelector = null);

assertVueContains

指定したVueコンポーネントのデータプロパティが配列で、指定値を含むことを宣言します。

$browser->assertVueContains($property, $value, $componentSelector = null);

assertVueDoesNotContain

指定したVueコンポーネントのデータプロパティが配列で、指定値を含まないことを宣言します。

$browser->assertVueDoesNotContain($property, $value, $componentSelector = null);

ページ

時にテストで、連続して実行する複雑なアクションをたくさん要求されることがあります。これにより、テストは読みづらく、また理解しづらくなります。ページに対し一つのメソッドを使うだけで、指定ページで実行されるアクションを記述的に定義できます。ページはまた、アプリケーションやシングルページで一般的なセレクタの、短縮記法を定義する方法も提供しています。

ページの生成

ページオプジェクトを生成するには、dusk:page Artisanコマンドを使います。すべてのページオブジェクトは、tests/Browser/Pagesディレクトリへ設置します。

php artisan dusk:page Login

ページの設定

デフォルトでページには、urlassertelementsの3メソッドが用意されています。urlassertメソッドは、この後説明します。elementsメソッドについては、のちほど詳細を紹介します。

urlメソッド

urlメソッドでは、そのページを表すURLのパスを返します。Duskはブラウザでこのページへ移動するとき、このURLを使用します。

/**
 * このページのURL取得
 *
 * @return string
 */
public function url()
{
    return '/login';
}

assertメソッド

assertメソッドでは、ブラウザが実際に指定ページを表示した時に、確認が必要なアサーションを定義します。このメソッドで完全に行う必要はありません。ですが、もしお望みであれば自由にアサートを記述してください。記述されたアサートは、このページへ移行時に自動的に実行されます。

/**
 * ブラウザがこのページにやって来たときのアサート
 *
 * @return void
 */
public function assert(Browser $browser)
{
    $browser->assertPathIs($this->url());
}

ページへのナビゲーション

ページの設定を終えたら、visitメソッドを使い、ページへ移行できます。

use Tests\Browser\Pages\Login;

$browser->visit(new Login);

すでに特定のページに移動済みで、現在のテストコンテキストへそのページのセレクタとメソッドを「ロード」する必要が起き得ます。この状況は、明示的に移動していなくても、あるボタンを押すことで指定ページへリダイレクトしてしまう場合に発生します。そうした場合は、onメソッドで、そのページをロードできます。

use Tests\Browser\Pages\CreatePlaylist;

$browser->visit('/dashboard')
        ->clickLink('Create Playlist')
        ->on(new CreatePlaylist)
        ->assertSee('@create');

セレクタの簡略記述

ページのelementsメソッドにより、覚えやすいCSSセレクタの短縮形を素早く定義できます。例として、アプリケーションのログインページの"email"入力フィールドの短縮形を定義してみましょう。

/**
 * ページ要素の短縮形を取得
 *
 * @return array
 */
public function elements()
{
    return [
        '@email' => 'input[name=email]',
    ];
}

これで、完全なCSSセレクタを指定する箇所ならどこでも、この短縮セレクタを使用できます。

$browser->type('@email', 'taylor@laravel.com');

グローバルなセレクタ簡略記述

Duskをインストールすると、ベースPageクラスがtests/Browser/Pagesディレクトリへ設置されます。このクラスは、アプリケーション全部のどのページからでも利用可能な、グローバル短縮セレクタを定義するsiteElementsメソッドを含んでいます。

/**
 * サイトのグローバル要素短縮形の取得
 *
 * @return array
 */
public static function siteElements()
{
    return [
        '@element' => '#selector',
    ];
}

ページメソッド

ページに対し定義済みのデフォルトメソッドに加え、テスト全体で使用できる追加メソッドも定義できます。たとえば、音楽管理アプリケーションを構築中だと想像してみましょう。アプリケーションのあるページでプレイリストを作成するのは、よくあるアクションです。各テストごとにプレイリスト作成のロジックを書き直す代わりに、ページクラスにcreatePlaylistメソッドを定義できます。

<?php

namespace Tests\Browser\Pages;

use Laravel\Dusk\Browser;

class Dashboard extends Page
{
    // 他のページメソッドの定義…

    /**
     * 新しいプレイリストの作成
     *
     * @param  \Laravel\Dusk\Browser  $browser
     * @param  string  $name
     * @return void
     */
    public function createPlaylist(Browser $browser, $name)
    {
        $browser->type('name', $name)
                ->check('share')
                ->press('Create Playlist');
    }
}

メソッドを定義すれば、このページを使用するすべてのテストの中で使用できます。ブラウザインスタンスは自動的にページメソッドへ渡されます。

use Tests\Browser\Pages\Dashboard;

$browser->visit(new Dashboard)
        ->createPlaylist('My Playlist')
        ->assertSee('My Playlist');

コンポーネント

コンポーネントはDuskの「ページオブジェクト」と似ていますが、ナビゲーションバーや通知ウィンドウのような、UI群と機能をアプリケーション全体で再利用するためのものです。コンポーネントは特定のURLと結びついていません。

コンポーネント生成

コンポーネントを生成するには、dusk:component Artisanコマンドを使用します。新しいコンポーネントは、tests/Browser/Componentsディレクトリに設置されます。

php artisan dusk:component DatePicker

上記の「デートピッカー」は、アプリケーション全体のさまざまなページで利用されるコンポーネントの一例です。テストスーツ全体の何ダースものテスト中で、日付を選択するブラウザ自動化ロジックを一々書くのは大変な手間です。その代わりに、デートピッカーを表すDuskコンポーネントを定義し、そうしたロジックをコンポーネントへカプセル化できます。

<?php

namespace Tests\Browser\Components;

use Laravel\Dusk\Browser;
use Laravel\Dusk\Component as BaseComponent;

class DatePicker extends BaseComponent
{
    /**
     * コンポーネントのルートセレクタ取得
     *
     * @return string
     */
    public function selector()
    {
        return '.date-picker';
    }

    /**
     * ブラウザページにそのコンポーネントが含まれていることをアサート
     *
     * @param  Browser  $browser
     * @return void
     */
    public function assert(Browser $browser)
    {
        $browser->assertVisible($this->selector());
    }

    /**
     * コンポーネントの要素のショートカットを取得
     *
     * @return array
     */
    public function elements()
    {
        return [
            '@date-field' => 'input.datepicker-input',
            '@year-list' => 'div > div.datepicker-years',
            '@month-list' => 'div > div.datepicker-months',
            '@day-list' => 'div > div.datepicker-days',
        ];
    }

    /**
     * 指定日付のセレクト
     *
     * @param  \Laravel\Dusk\Browser  $browser
     * @param  int  $year
     * @param  int  $month
     * @param  int  $day
     * @return void
     */
    public function selectDate($browser, $year, $month, $day)
    {
        $browser->click('@date-field')
                ->within('@year-list', function ($browser) use ($year) {
                    $browser->click($year);
                })
                ->within('@month-list', function ($browser) use ($month) {
                    $browser->click($month);
                })
                ->within('@day-list', function ($browser) use ($day) {
                    $browser->click($day);
                });
    }
}

コンポーネントの使用

コンポーネントを定義したら、全テスト中からデートピッカーの中の指定日付を簡単にセレクトできます。日付選択で必要なロジックに変更が起きたら、このコンポーネントを更新するだけです。

<?php

namespace Tests\Browser;

use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\Browser\Components\DatePicker;
use Tests\DuskTestCase;

class ExampleTest extends DuskTestCase
{
    /**
     * 基本的なコンポーネントテスト例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->browse(function (Browser $browser) {
            $browser->visit('/')
                    ->within(new DatePicker, function ($browser) {
                        $browser->selectDate(2019, 1, 30);
                    })
                    ->assertSee('January');
        });
    }
}

継続的インテグレーション

Note: 持続的インテグレーション設定ファイルを追加する前に、.env.testingファイルへhttp://127.0.0.1:8000の値のAPP_URLエントリがあることを確認してください。

CircleCI

DustテストにCircleCIを使用する場合、以下の設定ファイルを手始めに利用できます。TravisCIと同様に、php artisan serveコマンドを使用し、PHP組み込みWebサーバを起動できます。

version: 2
jobs:
    build:
        steps:
            - run: sudo apt-get install -y libsqlite3-dev
            - run: cp .env.testing .env
            - run: composer install -n --ignore-platform-reqs
            - run: php artisan key:generate
            - run: php artisan dusk:chrome-driver
            - run: npm install
            - run: npm run production
            - run: vendor/bin/phpunit

            - run:
                name: Start Chrome Driver
                command: ./vendor/laravel/dusk/bin/chromedriver-linux
                background: true

            - run:
                name: Run Laravel Server
                command: php artisan serve
                background: true

            - run:
                name: Run Laravel Dusk Tests
                command: php artisan dusk

            - store_artifacts:
                path: tests/Browser/screenshots

Codeship

DuskのテストをCodeshipで実行するには、以下のコマンドをCodeshipプロジェクトへ追加してください。以下のコマンドはひとつの参考例です。必要に応じて、自由にコマンドを追加してください。

phpenv local 7.2
cp .env.testing .env
mkdir -p ./bootstrap/cache
composer install --no-interaction --prefer-dist
php artisan key:generate
php artisan dusk:chrome-driver
nohup bash -c "php artisan serve 2>&1 &" && sleep 5
php artisan dusk

Heroku CI

DuskテストをHeroku CI上で実行するには、Herokuのapp.jsonファイルへ、以下のGoogle Chromeビルドパックとスクリプトを追加してください。

{
  "environments": {
    "test": {
      "buildpacks": [
        { "url": "heroku/php" },
        { "url": "https://github.com/heroku/heroku-buildpack-google-chrome" }
      ],
      "scripts": {
        "test-setup": "cp .env.testing .env",
        "test": "nohup bash -c './vendor/laravel/dusk/bin/chromedriver-linux > /dev/null 2>&1 &' && nohup bash -c 'php artisan serve > /dev/null 2>&1 &' && php artisan dusk"
      }
    }
  }
}

Travis CI

Travis CI上でDuskテストを実行するためには、以降の.travis.yml設定を使用してください。Travis CIはグラフィカルな環境ではないため、Chromeブラウザを実行するには追加の手順を行う必要があります。さらに、PHPの組み込みWebサーバを起動するために、php artisan serveを使用する必要もあるでしょう。

language: php

php:
  - 7.3

addons:
  chrome: stable

install:
  - cp .env.testing .env
  - travis_retry composer install --no-interaction --prefer-dist --no-suggest
  - php artisan key:generate
  - php artisan dusk:chrome-driver

before_script:
  - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
  - php artisan serve &

script:
  - php artisan dusk

GitHubアクション

Duskのテスト実行にGithubアクションを使う場合は、以下の設定ファイルを手始めに利用できます。TravisCIと同様に、PHPの組み込みサーバを起動するためにphp artisan serveコマンドが実行できます。

name: CI
on: [push]
jobs:

  dusk-php:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Prepare The Environment
        run: cp .env.example .env
      - name: Create Database
        run: mysql --user="root" --password="root" -e "CREATE DATABASE my-database character set UTF8mb4 collate utf8mb4_bin;"
      - name: Install Composer Dependencies
        run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader
      - name: Generate Application Key
        run: php artisan key:generate
      - name: Upgrade Chrome Driver
        run: php artisan dusk:chrome-driver
      - name: Start Chrome Driver
        run: ./vendor/laravel/dusk/bin/chromedriver-linux &
      - name: Run Laravel Server
        run: php artisan serve &
      - name: Run Dusk Tests
        run: php artisan dusk

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる