作成日: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; } }
- ▼routes/web.php or routes/api.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 { 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"