(ひっさびさにLaravel触っていて、キューのテストを書くときに詰まったので備忘録的な感じの内容です。)
前提
非同期で処理を行いたい時の手段として、Laravelではキューの仕組みを提供しています。
https://readouble.com/laravel/9.x/ja/queues.html
対象者がたくさんいる個別にメールを配信するシステムだったり、
個別に処理をしていく方が実装がシンプルになるような日次処理だったり、
色々な処理に活用されているかと思います。
背景
(ジョブのディスパッチ元でしてもいいのですが)、
ジョブの内部処理としてパラメータチェックを行っていたいなぁと以下のような処理を書いてました。
class HogeJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $param;
public function __construct(Param $param)
{
$this->param = $param; //チェックしたいパラメータ
}
public function handle()
{
if($this->validate($this->param))){
$this->fail();
}
// 後続の処理
}
private function validate(Param $param){
//バリデーション処理
}
}
この時、間違いなく$this->fail()
されていることを確認したいのですが、
QueueのMock、使えそうなものがないかなと探してみてもそれらしいものがありません。
https://readouble.com/laravel/9.x/ja/mocking.html
このケースに限らず、fail()に移行しているか確認したいケースはあるかと思うので、
「えぇ。。」
「failしたこと確認したいよぉ。。」
と、泥沼入りながら試行錯誤してみました。
結論
Event::fake([JobFailed::class])
を利用します。
$this->fail()
を呼び出すと、内部でIlluminate\Queue\Events\JobFailed
クラスのイベントを発火するため、
それが出たかどうかで判断します。
class HogeJobTest extends TestCase
{
public function test(){
Event::fake([JobFailed::class])
$param = new Param('適当な値');
HogeJob::dispatchSync($param);
Event::assertDispatched(JobFailed::class);
}
}
ちなみにEvent::assertDispatched
の第二引数で細かいエラー内容のハンドリングもできるため、
$this->fail('error');
みたいにした時に、エラーメッセージが想定通りか確認したいテストも記載可能です。
改善点
このパターンだとJob内部でDIされたオブジェクトをモックできないという欠点があります。
(HogeJobクラスのpublic function handle()
の引数に何かしらのインターフェースが指定されているようなパターン)
現状全く方法思いついていないので、困った時にまた検討していこうかなと。。