测试Eloquent Observer触发事件问题:使用Event::fake时Eloquent事件未触发
解决Eloquent Observer事件在Event::fake测试中不触发的问题
我之前也踩过类似的测试坑,咱们一步步拆解问题、解决它:
1. 核心问题:saved事件中isDirty()判断失效
在saved事件触发时,模型已经完成保存操作,此时模型的「脏属性」标记已经被重置,$job->isDirty('duration')会返回false,自然不会触发你的自定义事件。
解决方案:改用wasChanged('duration')方法,它专门用来检查模型在最近一次保存中是否修改了指定属性:
public function saved(Job $job) { if ($job->wasChanged('duration')) { // 替换isDirty为wasChanged event(new JobCustomerTotalAmountDueChanged($job)); } }
2. 确保Observer已正确注册
如果Observer根本没被Laravel识别,那所有逻辑都不会运行。检查你的服务提供者(比如App\Providers\AppServiceProvider.php)的boot方法里是否注册了Observer:
use App\Models\Job; use App\Observers\JobObserver; public function boot() { Job::observe(JobObserver::class); }
3. 正确使用Event::fake()避免拦截Eloquent模型事件
当你调用Event::fake()而不指定参数时,它会模拟所有事件的分发,包括Eloquent内部的saved/updated等模型事件。这会导致Observer无法接收到模型事件通知,进而不会触发你的自定义事件。
正确做法:只fake你需要测试的自定义事件,让Eloquent的模型事件正常触发:
public function test_duration_update_triggers_amount_due_event() { // 仅fake目标自定义事件,保留Eloquent模型事件的正常分发 Event::fake([JobCustomerTotalAmountDueChanged::class]); // 创建测试数据 $job = Job::factory()->create(['duration' => 5]); // 更新duration属性 $job->update(['duration' => 10]); // 断言自定义事件被正确触发,并且传递了正确的Job实例 Event::assertDispatched(JobCustomerTotalAmountDueChanged::class, function ($event) use ($job) { return $event->job->id === $job->id; }); }
4. 避免批量更新导致模型事件不触发
如果你的测试中使用了批量更新语法(比如Job::where('id', $jobId)->update(['duration' => 10])),这种方式会直接执行数据库查询,不会实例化模型,自然不会触发任何模型事件和Observer逻辑。
确保测试中使用模型实例的更新方法:
// 正确:实例化模型后更新 $job->update(['duration' => 10]); // 或者 $job->duration = 10; $job->save();
内容的提问来源于stack exchange,提问作者Amade
相关产品推荐
相关产品推荐

