如何在Laravel中用PHPUnit测试Bus::chain()->catch()异常处理逻辑
测试Laravel链式任务的Catch代码块
问题场景
我正在使用PHPUnit编写测试,验证链式任务的调度逻辑,已经能通过Bus::assertChained()验证任务的链式结构,但遇到了一个问题:当链式任务中的某个任务抛出异常时,如何触发对应的catch()代码块,并断言$model->error字段会被更新?
示例业务代码:
Bus::chain([ new JobOne($model), new JobTwo($model), new JobThree($model), ]) ->catch(function (Throwable $e) use ($model) { $model->update([ 'error' => true, ]); }) ->dispatch();
由于任务都是直接实例化的新对象,无法直接通过Mock模拟抛出异常,需要找到可行的触发方式。
解决方案
方法一:利用Bus Fake机制自定义任务行为
Laravel的Bus::fake()可以拦截任务执行,针对特定任务类设置自定义逻辑,强制抛出异常:
public function test_chain_catch_triggers_on_job_failure() { $model = Model::factory()->create(); // 指定JobTwo执行时抛出异常 Bus::fake([ JobTwo::class => fn ($job) => throw new Exception('Test exception'), ]); // 执行触发链式任务的业务逻辑(比如调用接口或方法) $this->post('/trigger-chain-job'); // 验证catch块执行结果 $model->refresh(); $this->assertTrue($model->error); // 可选:验证链式任务的调度顺序 Bus::assertChained([JobOne::class, JobTwo::class]); }
方法二:给任务类添加测试专用异常开关
在任务类中添加静态属性,仅在测试环境下控制是否抛出异常:
修改JobTwo类:
class JobTwo implements ShouldQueue { // 测试专用开关 public static $throwTestException = false; public function __construct(public Model $model) {} public function handle() { // 仅在测试环境且开关开启时抛出异常 if (self::$throwTestException && app()->environment('testing')) { throw new Exception('Test failure'); } // 原任务业务逻辑 } }
测试代码:
public function test_catch_block_updates_model_on_failure() { $model = Model::factory()->create(); JobTwo::$throwTestException = true; // 调度链式任务 Bus::chain([ new JobOne($model), new JobTwo($model), new JobThree($model), ])->dispatch(); // 验证模型字段更新 $model->refresh(); $this->assertTrue($model->error); // 重置开关,避免影响其他测试 JobTwo::$throwTestException = false; }
方法三:Partial Mock任务类的handle方法
使用PHPUnit的部分Mock功能,Mock任务类的handle方法使其抛出异常:
public function test_chain_job_failure_triggers_catch() { $model = Model::factory()->create(); // Partial Mock JobTwo,仅替换handle方法 $this->partialMock(JobTwo::class, function ($mock) { $mock->shouldReceive('handle') ->andThrow(new Exception('Test error')); }); // 调度链式任务 Bus::chain([ new JobOne($model), new JobTwo($model), new JobThree($model), ])->dispatch(); // 验证模型状态 $model->refresh(); $this->assertTrue($model->error); }
内容的提问来源于stack exchange,提问作者Dan Breakwell
相关产品推荐
相关产品推荐

