Laravel 9测试命令发送通知时Notification::assertSentTo失效
问题原因及解决方法
可能的原因
Notification::fake()调用时机错误
如果在执行Artisan::call()之后才调用Notification::fake(),fake的拦截逻辑不会对已执行的命令生效,自然捕获不到通知发送事件。命令内的通知发送未走Laravel通知系统
若命令里直接用Mail类发送邮件、或实例化底层发送类而非通过Notification门面/模型的notify()方法,Notification::fake()无法拦截这类绕过系统的发送行为。测试数据不符合发送条件
手动运行命令用的是真实环境中符合条件的数据,但测试时未创建匹配命令筛选规则的测试模型(比如即将到期的试用用户),导致命令执行时根本没有触发通知发送。队列配置未在测试上下文生效
即使phpunit配置了QUEUE_CONNECTION=sync,若命令里硬编码了队列连接(比如->onQueue('xxx')),或测试环境的配置未覆盖命令执行时的上下文,通知会被推送到队列而非同步执行,fake无法捕获异步队列里的通知。
正确测试方式
1. 确保Fake时机正确
先调用Notification::fake(),再执行命令,最后做断言:
public function test_trial_expire_notification_command() { // 先启动通知Fake Notification::fake(); // 创建符合命令筛选条件的测试用户 $user = User::factory()->create([ 'trial_ends_at' => now()->addDays(3), // 匹配命令中"3天内到期"的规则 ]); // 执行目标artisan命令 $this->artisan('your:command-name')->assertExitCode(0); // 断言通知已发送给目标用户 Notification::assertSentTo( $user, TrialNextToExpireNotification::class ); }
2. 检查命令内的通知发送逻辑
确保命令里使用Laravel标准的通知发送方式:
// 推荐方式:通过模型的notify方法 $user->notify(new TrialNextToExpireNotification()); // 或通过Notification门面批量发送 Notification::send($eligibleUsers, new TrialNextToExpireNotification());
避免直接调用Mail::send()等绕过通知系统的代码。
3. 验证队列配置与命令逻辑
- 在
phpunit.xml中确认队列连接配置:<env name="QUEUE_CONNECTION" value="sync"/> - 命令内不要硬编码队列连接,移除类似
->onQueue('default')的强制队列设置。 - 检查命令中是否有环境判断逻辑(比如
if (app()->isProduction())),避免测试环境下跳过通知发送。
4. 调试命令的筛选逻辑
若断言仍失败,可在命令中临时添加调试代码,确认是否查询到目标用户:
// 在命令的handle方法中添加 $eligibleUsers = User::where('trial_ends_at', '<=', now()->addDays(3))->get(); dump($eligibleUsers->count()); // 测试时会输出查询到的用户数量
确保测试数据能被命令的筛选规则命中。
内容的提问来源于stack exchange,提问作者Juan M. Fernandez
相关产品推荐
相关产品推荐

