laravel: duskのメモ

ごった煮

docker: seleniumコンテナはどのイメージを使えばいいか

最新版のだとダメだった。
いろいろ試して selenium/standalone-chrome:3.141.59-oxygen が上手く動作した。
(duskのバージョンにも依存するのか?)

ポートは 4444:4444 でok
RemoteWebDriver には http://{seleniumコンテナのname}:4444/wd/hub を指定

テスト毎に初期レコードを投入した状態にロールバックする

達成したい目標は「テストが終わるたびにいくつかの初期レコードを投入した状態に戻したい」というもの。
具体的にはマスタテーブルにある程度のレコードを入れたいとか、ログイン用のユーザを作っておくとか。
しかし、デフォルトの DatabaseTransactions, DatabaseMigrations, RefreshDatabase では上手く達成できなかった。
もうめんどくさいから投入したいレコードを SQL ファイルでまとめて読み込むようにした方が早いんじゃないかと思う。

storageの配下に以下の物を配置します。

  • storage/tests/init.sql: テーブルをCREATEしたり初期レコードをINSERTするSQL
  • storage/tests/migrations/InitializeDb.php: SQLを用いてDBを初期化するマイグレーションファイル
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
 
class InitializeDb extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        DB::unprepared(file_get_contents(storage_path('tests/init.sql')));
    }
 
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        //
    }
}

上記ファイルを用いてマイグレーションするため、DatabaseMigrations を継承して runDatabaseMigrations 関数をオーバーライドします。

// tests/DatabaseMigrations.php

trait DatabaseMigrations
{
    use BaseDatabaseMigrations;
 
    /**
     * Refresh a conventional test database.
     *
     * @return void
     */
    protected function runDatabaseMigrations()
    {
        $this->artisan('migrate:fresh', [
            '--path' => storage_path('tests/migrations'),
            '--realpath' => 1,
        ]);
 
        $this->app[Kernel::class]->setArtisan(null);
 
        $this->beforeApplicationDestroyed(function () {
            RefreshDatabaseState::$migrated = false;
        });
    }
}

SQLファイルの実行に失敗する

DBeaver(+MySQLバイナリ)でダンプしたSQLファイルをマイグレーションで利用した際、何個かエラーが出ました。
以下、雑な対処です。

  • LOCK TABLES 構文を削除する
  • Generated Column が含まれている時、DEFAULT を INSERT する

テスト毎にcookieを初期化

ログイン状態が継続してしまったりするので、cookieをテスト完了ごとにきっちり削除する。
DuskTestCaseの setUp() に以下を記述します。

public function setUp() : void
{
    parent::setUp();

    foreach (static::$browsers as $browser) {
        $browser->driver->manage()->deleteAllCookies();
    }
}

プルダウンのoptionのvalueを取得する

WebElement に対して findElements (または findElement) するだけ

use Facebook\WebDriver\WebDriverBy;

$this->browse(function (Browser $browser) {
    $select = $browser->element('select[name="area_id"]');
    $options = $select->findElements(WebDriverBy::cssSelector('option:not([disabled])'));

    $values = collect([]);
    foreach ($options as $option) {
        $value = (int) $option->getAttribute('value');
        $values->push($value);
    }
});

elementのテキスト部分が取得したい

$element->getText()

CSSセレクタでand条件使いたい

[] を連立するとできた

$element->findElements(WebDriverBy::cssSelector('input[type="hidden"][name$="[token]"]'));

selectタグの特定optionを選択させたい

ラベルを sendKeys で指定

$element->findElements(WebDriverBy::cssSelector('select[name$="[pref]"]'))->sendKeys('東京都');