Laravel アプリケーションを使用する場合、コマンドが負荷の高いタスクを実行する必要があるシナリオに遭遇するのが一般的です。メインプロセスのブロックを避けるために、キューで処理できるジョブにタスクをオフロードすることを決定することもできます。
例を見てみましょう。コマンド app:import-users が大きな CSV ファイルを読み取り、エントリごとにユーザーを作成する必要があると想像してください。コマンドは次のようになります:
/* ImportUsersCommand.php */ namespace App\Console\Commands; /*...*/ class ImportUsersCommand extends Command { protected $signature = 'app:import-users'; public function handle() { dispatch(new ImportUsersJob()); $this->line('Users imported successfully.'); $this->line('There are: ' . User::count(). ' Users.'); } }
この例では、コマンドはファイルの読み取りとユーザーの作成を処理するジョブをディスパッチします。 ImportUsersJob.php は次のようになります:
/* ImportUsersJob.php */ namespace App\Jobs; /*...*/ class ImportUsersJob implements ShouldQueue { public function handle(FileReader $reader): void { foreach($reader->read('users.csv') as $data) { User::create([ 'name' => $data['name'], 'email' => $data['email'], ]); } } }
この機能をテストする場合、コマンドの一般的なテストは次のようになります:
/* ImportUsersCommandTest.php */ namespace Tests\Feature; /*...*/ class ImportUsersCommandTest extends TestCase { use RefreshDatabase; public function test_it_processes_the_file(): void { Storage::fake('local')->put('users.csv', "..."); $this->artisan('app:import-users') ->expectsOutput('Users imported successfully.') ->expectsOutput('There are: 10 Users.') ->assertSuccessful(); $this->assertDatabaseCount('users', 10); } }
一見すると、このテストは完全に機能しているように見えます。テスト スイートを実行すると、成功した結果が表示されます:
ただし、実際の環境で app:import-users コマンドを実行すると、予期しない結果が得られる可能性があります:
ご覧のとおり、コマンド出力はデータベースにユーザーが 0 人しかいないことを示しています。では、なぜこのようなことが起こるのでしょうか?
その理由は、ジョブがキューにディスパッチされるため、コマンドの実行と同期して実行されないためです。ユーザーは、後でキューがジョブを処理するときにのみ作成されます。
テスト スイートはデフォルトで同期キュー ドライバーを使用します。つまり、ジョブはテスト中に同期的に処理されます。その結果、ジョブはすぐに実行され、すべてが期待どおりに機能していることがわかります。
この動作はテスト環境では許容されますが、実際の結果は運用環境の QUEUE_CONNECTION 構成に依存することを認識することが重要です。また、プロジェクトの要件を考慮すると、ジョブが非同期キューで処理されることがわかっているかもしれません。
この違いを理解したら、「偽陽性」を避けるためにテストを改善したくなるかもしれません。
まず、ジョブが同期的に処理されるか非同期的に処理されるかに関係なく、コマンドが実際にジョブをディスパッチすることを確認することが重要です。それをテストする方法は次のとおりです:
/* ImportUsersCommandTest.php */ namespace Tests\Feature; /*...*/ class ImportUsersCommandTest extends TestCase { public function test_it_dispatches_the_job(): void { Queue:fake(); $this->artisan('app:import-users') ->expectsOutput('Process has been queued.') ->assertSuccessful(); Queue::assertPushed(ImportUsersJob::class); } }
ジョブがディスパッチされたことを確認したら、別のテストでジョブによって実行される実際の作業をテストできます。ジョブのテストを構成する方法は次のとおりです:
/* ImportUsersJobTest.php */ namespace Tests\Feature; /*...*/ class ImportUsersJobTest extends TestCase { use refreshDatabase; public function test_it_processes_the_file() { Storage::fake('local')->put('users.csv', "..."); app()->call([new ImportUsersJob(), 'handle']); $this->assertDatabaseCount('users', 10); } }
これにより、ジョブがキューによって処理されるか同期的に処理されるかに関係なく、必要な作業が確実に実行されます。
現実世界と同様に、特殊なケースが発生する可能性があるため、そのような事態に備えておく必要があります。
Laravel のキュー システムは、ワーカーの設定に従って、例外が発生するとジョブを再試行します。再試行回数を超えると、ジョブは失敗としてマークされます。
では、ファイルが存在しない場合はどうなるでしょうか?入力を検証し、必要に応じて例外をスローすることで、このようなエッジケースに対処する必要があります。
仕事でこれに対処する方法は次のとおりです:
/* ImportUsersJobTest.php */ namespace App\Jobs; /*...*/ class ImportUsersJob implements ShouldQueue { use Queueable; public function handle(FileReader $reader): void { if(!Storage::disk('local')->exists('users.csv')){ throw new Exception('The users.csv file doesn\'t exist.') } foreach($reader->read('users.csv') as $data) { User::create([ 'name' => $data['name'], 'email' => $data['email'], ]); } } }
このシナリオをテストする方法は次のとおりです:
/* ImportUsersJobTest.php */ namespace Tests\Feature; /*...*/ class ImportUsersJobTest extends TestCase { use refreshDatabase; /*...*/ public function test_it_fails_when_file_doesnt_exist(): void { Storage::fake('local'); $this->expectException(Exception::class); $this->expectExceptionMessage('The users.csv file doesn\'t exist.'); dispatch(new ImportUsersJob()); } }
このアプローチにより、テストが実際の世界でジョブがどのように処理されるかをより正確に反映することが保証されます。
コントローラーがジョブをキューにディスパッチする場合、またはイベント リスナーがキューに登録されている場合にも、同じ戦略を適用できます。
いつものように、プロジェクトやチームに合わせてこれらのプラクティスを調整してください。
ぜひご意見をお聞かせください!
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3