laravel artisanコマンドでplaywrightを実行

laravel環境のサイトなので色々とlaravelを使いたい

作成日:2025-04-24, 更新日:2025-04-24

経緯

テスト用の各入力データはユニット・機能テスト用にlaravel・PHPで用意済み
playwrightはjavascriptで記述するので、どうにかしてlaravel・PHPで設定したデータを取ってきたい
ついでにartisanコマンドで実行したい

概要

ざっくりとやることは3つ

  • laravelの中にブラウザテスト(playwright)を追加
  • playwrightでブラウザテストを実行する際、共有データを取得
  • artisanコマンドで実行

laravelの中にブラウザテスト(playwright)を追加

「tests/」フォルダの下でplaywrightのプロジェクトフォルダを作成
※laravelのduskがbrowserフォルダになるっぽいので、区別したい → 「tests/Web」にしておく

playwrightの初期化

tests/Webを作成して、その中で実行したんだけど...本当は違う

▼laravelのルートディレクトリで実行

> npm init playwright@latest
  • tests/Webを作成して、その中で実行 ← こっちでやったらあとでエラーになった...
  • laravelのルートディレクトリで実行

テストファイル

テストファイルの置き場所は情報共有や諸々検討した結果、初期のままでいく
→「tests/Web/tests」の中の「xx.spec.js」を実行

playwrightでブラウザテストを実行する際、共有データを取得

ブラウザテストで使用するデータは...ブラウザテストのソースの直書きとか別途用意というのは避けたいし、ユニット・機能・ブラウザテストのそれぞれで使い回せるようにしたい

データの取得方法は

  • PHP、javascriptのそれぞれで読み取れるようなテキストファイル(※json形式など)を用意して、取得
  • PHPで用意して、playwrightではAPIを使って取得

とあるが、APIで取得することを選択する

希望

テストで使うデータをPHP(laravel)で用意して、playwrightではAPIを使って取得する
データ自体はlaravelの「tests/」フォルダの中に置いておきたい
さらにテストファイルである「tests/Web/tests/xx.spec.js」と同じトコに置いておきたい
ただ、調べるとブラウザから「tests/Web」配下に直接アクセスさせようとするとcomposer.jsonを触る必要があるとか...気持ち的に面倒なので別で受けて「tests/Web」の中にアクセスする

「https://example.test/api/dataset」のアクセスを

  • 「tests/Web/dataset.php」で受けたいならcomposer.jsonを触る必要がある
  • 「app/hoge/dataset.php」で受けたいならcomposer.jsonを触る必要が無い

APIのエンドポイントの例

取得したいデータとAPIのエンドポイントの組み合わせは機能ごとにエンドポイントを設定し、必要なデータの種類はgetパラメータで受けたい

  • ログイン機能
    • エンドポイント: login/dataset
    • 正常系データ: ?data=ok
    • 異常系データ: ?data=err
  • 会員登録機能
    • エンドポイント: register/dataset
    • 正常系データ: ?data=ok
    • 異常系データ: ?data=err

やることは...

  • ルーティング設定
  • 受け手から「tests/」にあるデータを取ってくる

ルーティング設定

local、testingでのみ有効とし、レスポンスはjson形式で返す
getパラメータを使いたいので「Request $request」を渡す

▼routes/web.php or routes/api.php

if (app()->environment(['local', 'testing'])) {
	Route::get('login/dataset', function (Request $request) {
		return response()->json(App\Hoge\Login::getData($request));
	});
	// Route::get('foo', function (Request $request) {
	// 	return response()->json(App\Hoge\Foo::getData($request));
	// });
}

受け手から「tests/」にあるデータを取ってくる

<?php
namespace App\Hoge;

use Illuminate\Http\Request;
use Tests\Web\Tests as WebTest;

class Login
{
	public static function getData(Request $request): array
	{
		$dataType = $request->query('data');

		switch ( $dataType ) {
			case 'ok':
				$dataset = WebTest\LoginDataset::okDataset();
			break;

			case 'err':
				$dataset = WebTest\LoginDataset::errDataset();
			break;

			default:
				$dataset = array(
					'error' => 'Invalid data type',
				);
			break;
		}

		return $dataset;
	}
}

あとは「Tests\Web\Tests\LoginDataset.php」を用意してデータを返せばOK

artisanコマンドで実行

専用に用意する必要があるとのこと

  • Artisanコマンドを作成
  • コマンドクラスを編集
  • app/Console/Kernel.php に登録

Artisanコマンドを作成

> php artisan make:command RunPlaywrightTest

→「app/Console/Commands/RunPlaywrightTest.php」が作成される

コマンドクラスを編集

▼app/Console/Commands/RunPlaywrightTest.php

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Symfony\Component\Process\Process;

class RunPlaywrightTest extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'playwright:test {path?} {--args=*}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Run Playwright tests (optionally specify path and args)';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $path = $this->argument('path') ?? 'tests/Web';
        $args = $this->option('args'); // 例: ['--project=chromium']

        $this->info("Running Playwright tests in: $path");

        $command = array_merge(['npx', 'playwright', 'test', $path], $args);
        $process = new Process($command);
        $process->setTimeout(null);
        $process->run(function ($type, $buffer) {
            echo $buffer;
        });

        return $process->isSuccessful() ? Command::SUCCESS : Command::FAILURE;
    }
}

app/Console/Kernel.php に登録

// 省略
class Kernel extends ConsoleKernel
{
    protected $commands = [
        \App\Console\Commands\RunPlaywrightTest::class,
    ];
    
    // 省略

実行

▼「tests/Web/tests」内にある「xxx.spec.js」のすべてを実行

> php artisan playwright:test

▼対象指定

> php artisan playwright:test tests/Web/tests/Login.spec.js

エラーになった...

    at Object. (C:\xxx\tests\Web\tests\example.spec.js:4:5)
Error: Playwright Test did not expect test.beforeEach() to be called here.
Most common reasons include:
- You are calling test.beforeEach() in a configuration file.
- You are calling test.beforeEach() in a file that is imported by the configuration file.
- You have two different versions of @playwright/test. This usually happens
  when one of the dependencies in your package.json depends on @playwright/test.

playwrightの初期化の方法が間違っていたらしい...

対応1: tests/Webからいくつか削除

> rm -rf node_modules package-lock.json package.json

対応2: laravelのルートで実行

> npm init -y
> npm install --save-dev @playwright/test
> npx playwright install

対応3: playwright.config.jsの修正

testDir: './tests/Web/tests',

→これなら「testDir: './tests/Web',」とすれば「/tests/Web」配下に各テストファイルを置くことが可能

正しい手順

laravelのルート直下で実行していなかったので...面倒なことになった

  • ▼laravelのルートディレクトリで実行
    > npm init playwright@latest
  • APIを用意
    • ▼routes/web.php or routes/api.php
      if (app()->environment(['local', 'testing'])) {
      	Route::get('login/dataset', function (Request $request) {
      		return response()->json(App\Hoge\Login::getData($request));
      	});
      	// Route::get('foo', function (Request $request) {
      	// 	return response()->json(App\Hoge\Foo::getData($request));
      	// });
      }
    • 受け口の設定
      <?php
      namespace App\Hoge;
      
      use Illuminate\Http\Request;
      use Tests\Web\Tests as WebTest;
      
      class Login
      {
      	public static function getData(Request $request): array
      	{
      		$dataType = $request->query('data');
      
      		switch ( $dataType ) {
      			case 'ok':
      				$dataset = WebTest\LoginDataset::okDataset();
      			break;
      
      			case 'err':
      				$dataset = WebTest\LoginDataset::errDataset();
      			break;
      
      			default:
      				$dataset = array(
      					'error' => 'Invalid data type',
      				);
      			break;
      		}
      
      		return $dataset;
      	}
      }
  • artisanコマンドの準備
    • > php artisan make:command RunPlaywrightTest

      →「app/Console/Commands/RunPlaywrightTest.php」が作成される
      ▼app/Console/Commands/RunPlaywrightTest.phpを修正

      <?php
      
      namespace App\Console\Commands;
      
      use Illuminate\Console\Command;
      use Symfony\Component\Process\Process;
      
      class RunPlaywrightTest extends Command
      {
          protected $signature = 'playwright:test {path?} {--args=*}';
          protected $description = 'Run Playwright tests (optionally specify path and args)';
      
          /**
           * Execute the console command.
           *
           * @return int
           */
          public function handle()
          {
              $path = $this->argument('path') ?? 'tests/Web';
              $args = $this->option('args'); // 例: ['--project=chromium']
      
              $this->info("Running Playwright tests in: $path");
      
              $command = array_merge(['npx', 'playwright', 'test', $path], $args);
              $process = new Process($command);
              $process->setTimeout(null);
              $process->run(function ($type, $buffer) {
                  echo $buffer;
              });
      
              return $process->isSuccessful() ? Command::SUCCESS : Command::FAILURE;
          }
      }
    • app/Console/Kernel.php に登録
      // 省略
      class Kernel extends ConsoleKernel
      {
          protected $commands = [
              \App\Console\Commands\RunPlaywrightTest::class,
          ];
          
          // 省略
  • 実行
    ▼「tests/Web」内にある「xxx.spec.js」のすべてを実行

    > php artisan playwright:test

    ▼対象指定

    > php artisan playwright:test tests/Web/Login.spec.js

コマンド

基本

全部実行

> php artisan playwright:test

対象ファイルのみ実行

> php artisan playwright:test tests/Web/tests/login.spec.js

chromeだけ実行

全てのテストをchromeで実行

> php artisan playwright:test --args="--project=chromium"

※「--args」の中身をplaywrightの引数として渡す
※複数渡すときは「 --args="--project=chromium" --args="--xxx=xxx"」みたいな感じ

対象ファイルのみchromeで実行

> php artisan playwright:test tests/Web/tests/login.spec.js --args="--project=chromium"