Laravel Dusk 事始め [Snipe-ITを例に]
Snipe-IT という知る人ぞ知るIT資産管理ツールがあります。
2020/10/26 に v5.0.4 がリリースされ、継続的にアップデートされているこのツール、Laravel 6 ベースで、ブランチリストを眺めたところ dusk
というのがありました。見た感じ、E2Eテストを Codeception から切り替えてみようと試みを始めたのではないかと思いました。
しかしE2Eテストコードがあるはずの tests/Browser
ディレクトリは見事にほぼカラでプロジェクト中断?のような感じがしたのですが、かえってこれから Laravel Dusk でCIを整備していくというときの"素振り素材"にはちょうど良さそうに思ったので、その始め方を記事としてまとめてみます。
概要
- オリジナルのコードを fork して少し調整する
- docker-compose の準備と起動
- 初期データの投入
- Dusk Test の実行
試行環境
- macOS Catalina 10.15.7
- Docker Desktop for Mac 2.5.0.0(49427)
- Engine 19.03.13
- Compose 1.27.4
- Snipe-IT v5.0.5-pre build 5457
- PHP 7.2.24
- Laravel 6.18.14
- Dusk 6.8
詳細
オリジナルのコードを fork して少し調整する
以下の手順で Dusk が実行可能となるように調整するのが良さそうです
-
https://github.com/snipe/snipe-it を fork して dusk ブランチに develop ブランチを取り込む
- fork は リポジトリ右上の Fork ボタンを押して進めればOK
- fork後の snipe-it リポジトリを作業ディレクトリに移して、以下の手順でコマンドを実行
cd /path/to/snipe-it
git switch develop
git switch dusk
git pull origin develop
- コンフリクトが起きるので解決する
- オリジナルの最新 develop コードを採用して Resolved としました
- 2020/11/8 時点ではコンフリクトは1ファイル [1]
- オリジナルの最新 develop コードを採用して Resolved としました
- Dockerfile の composer install 時にある
--no-dev
を無効化-
Laravel 6.x Laravel Dusk のガイド「インストール」 の Note にある通り、本番環境にDuskをインストールするのは「御法度」で、Dusk を実行するには development モードとするのが適切なので、Dusk 用の Dockerfile.dusk をコピーして作成し以下の箇所を変更
-
Dockerfile.diff
- RUN composer install --no-dev --working-dir=/var/www/html + RUN composer install --working-dir=/var/www/html
-
-
Laravel 6.x Laravel Dusk のガイド「インストール」 の Note にある通り、本番環境にDuskをインストールするのは「御法度」で、Dusk を実行するには development モードとするのが適切なので、Dusk 用の Dockerfile.dusk をコピーして作成し以下の箇所を変更
- tests/DuskTestCase.php の調整
- 諸説ありますが[2]、おそらく手数として最小な selenium/standalone-chrome イメージを利用する方法を今回は選択するので、それに即した調整です
-
DuskTestCase.php.diff.1
:--no-sandbox
オプション追加$options = (new ChromeOptions)->addArguments([ '--disable-gpu', '--headless', '--window-size=1920,1080', + '--no-sandbox', ]);
-
DuskTestCase.php.diff.2
: seleniumサーバーのURLを変更 [3]return RemoteWebDriver::create( - //'http://localhost:9515', + 'http://selenium:4444/wd/hub', DesiredCapabilities::chrome()->setCapability( ChromeOptions::CAPABILITY, $options ) );
-
- 諸説ありますが[2]、おそらく手数として最小な selenium/standalone-chrome イメージを利用する方法を今回は選択するので、それに即した調整です
docker-compose の準備と起動
複数のコンテナを用いるので docker-compose で管理するのが良いと思うのですが、オリジナルには存在しないので自分で整備する必要があります
-
必要な環境変数を定義する
-
定義が必要な設定例は以下資料の Mysql,Email,Snipe-IT を参考に
env-file example
(折りたたみ)のような内容になります(// 以降のコメントは削除してご利用ください)
https://snipe-it.readme.io/docs/docker#dockers-environment-variablesenv-file example
# Mysql Parameters MYSQL_PORT_3306_TCP_ADDR=snipe-mysql // 必要。docker-compose.yml に合わせる MYSQL_PORT_3306_TCP_PORT=3306 // なくてもOK MYSQL_ROOT_PASSWORD=YOUR_SUPER_SECRET_PASSWORD MYSQL_DATABASE=snipeit MYSQL_USER=snipeit MYSQL_PASSWORD=YOUR_snipeit_USER_PASSWORD # Email Parameters MAIL_PORT_587_TCP_ADDR=smtp.whatever.com MAIL_PORT_587_TCP_PORT=587 MAIL_ENV_FROM_ADDR=youremail@yourdomain.com MAIL_ENV_FROM_NAME=Your_Full_Email_Name MAIL_ENV_ENCRYPTION=tcp MAIL_ENV_USERNAME=your_email_username MAIL_ENV_PASSWORD=your_email_password # Snipe-IT Settings APP_ENV=development // production から変更 APP_DEBUG=true // false から変更 APP_KEY=<<Fill in Later!>> // php artisan key:generate で生成 APP_URL=http://selenium // 必要。docker-compose.yml に合わせる APP_TIMEZONE=Asia/Tokyo APP_LOCALE=en
-
オリジナルの Dockerfile に以下の記述があり、
COPY docker/docker.env /var/www/html/.env
つまり、Laravelが参照する .env の内容はこの docker.env のものになります(それを踏まえて適宜カスタマイズを行えます)
また、Laravelが参照する .env と区別する意図で、環境変数を定義したファイルは以降.env.docker-compose
と記述します
-
-
docker-compose.yml を準備する
-
本体(snipe-it)とDB(snipe-mysql)に加えて、selenium service を追加し、
docker-compose.yml example
(折りたたみ)のような内容になりますdocker-compose.yml example
version: '3' services: snipe-mysql: container_name: snipe-mysql image: mysql:5.6 env_file: - ./.env.docker-compose volumes: - snipesql-vol:/var/lib/mysql command: --default-authentication-plugin=mysql_native_password expose: - "3306" snipe-it: build: context: ./ dockerfile: Dockerfile.dusk env_file: - ./.env.docker-compose volumes: - ./composer.json:/var/www/html/composer.json - ./composer.lock:/var/www/html/composer.lock - ./storage:/var/www/html/storage - ./tests/Browser/screenshots:/var/www/html/tests/Browser/screenshots ports: - "3051:80" depends_on: - snipe-mysql selenium: image: selenium/standalone-chrome ports: - "4444:4444" volumes: snipesql-vol:
-
snipe-mysql service はおそらく他に見つかる情報とほぼ同様な内容ですが、
snipe-it service に関しては以下の点が独特です- Dockerfile.dusk を build する(
--no-dev
をはずしている) - composer.json, composer.lock, storage, screenshots ディレクトリ を volumes でマウントしている
- これはローカルから変更内容やログやスクリーンショットを参照したいケースがあり得ると想定した設定です
- Dockerfile.dusk を build する(
-
-
docker-compose を起動する
-
docker-compose up -d
で環境一式を起動 - 変更があったときなどは適宜
docker-compose up -d --build
- 自分の MacBook Air (macOS Catalina 10.15.7 メモリ 16G) では 6分とちょっとで起動完了
-
docker-compose exec snipe-it bash
でコンテナ内に入り操作が行える- 必要に応じて以下のようなキャッシュクリアコマンド等を実行できます
composer dump-autoload
composer clear-cache
php artisan view:clear
php artisan route:clear
php artisan clear-compiled
php artisan config:cache
php artisan cache:clear
- 必要に応じて以下のようなキャッシュクリアコマンド等を実行できます
-
初期データの投入
環境が起動完了したら、初期データの投入を通常であろうセットアップウィザードではなくスクリプトで行います
セットアップウィザードのソースコードをひも解いた結果、次の順に実行するのが妥当そうです
- Step 1 : migrate (スキーマ投入)
php artisan migrate --force
- Step 2: Laravel Passport のインストール
php artisan migrate --path='vendor/laravel/passport/database/migrations' --force
php artisan passport:install
- DatabaseSeeder を流す(4〜5分)
php artisan db:seed
-
Faker で作成されたランダムなユーザ名をテストしやすいように
admin
に変更
php artisan db:seed --class=AdjustFirstAdminUserSeeder
http://localhost:3051/ をリクエストしてログインするとこんな画面
Dust Test の実行
すでに存在している tests/Browser/ExampleTest.php
(タイトルにアプリケーション名が含まれているかを検証)に、以下のログインのテストコードを加えて php artisan dusk
実行してみましょう
ログインのテストコード と 実行時のコンソール出力
public function test_Login()
{
$this->browse(function (Browser $browser) {
$browser->visit('/login')
->type('username', BrowserTestConst::USERNAME)
->type('password', BrowserTestConst::PASSWORD)
->press('Login')
->waitForText('Snipe-IT Demo')
->assertSee('Snipe-IT Demo')
->assertSee('Dashboard')
->assertSee('Recent Activity')
->assertSee('Assets by Status')
->assertSee('Asset Categories')
;
});
}
- 意図的に失敗するように、パスワードを書き換えたり想定されている文字列を変えたりしたうえで変えたファイルをコンテナ側に
docker cp
で転送し、キャッシュのクリアを行って再度php artisan dusk
を実行すると、失敗時のスクリーンショットが残り、それを確認できます
まとめ・その他
ここまでお付き合いいただきありがとうございます。
Snipe-IT というOSSを題材に、Laravel Dusk を利用した E2E テストを始めてみる例を書きました。
実装コードは https://github.com/sogaoh/snipe-it/tree/dusk に公開しているので自由に参照してください。本記事を書いた時点での Tag も作成してあります。
次回は E2E テスト を整備していくときのコツを紹介できればと考えています。
その次に CI にしていくときのあれこれも書いていこうと思っています。
phpcon2020 で
「Dusk で CI」にまつわる話をします。関心あればご視聴ください。エントリーは こちら からになるようです。
-
「dusk selenium」で検索すると複数のやり方が出てきます ↩︎
-
https://github.com/php-webdriver/php-webdriver/blob/main/lib/Remote/RemoteWebDriver.php#L88 ↩︎
Discussion