Laravel订单更新通知测试失败,请求排查问题
Laravel订单更新通知测试失败问题排查与修复
问题描述
测试Laravel应用中OrderController的更新功能时,需要验证订单更新通知是否成功发送。控制器通过OrderService调用order->notify()方法发送OrderUpdatedNotification,测试中已使用Mail::fake()和Notification::fake(),但断言通知发送时失败。
相关代码
Order模型代码
class Order extends Model { use Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ // ... 'member_id', // ... ]; public function member(): BelongsTo { return $this->belongsTo(Member::class, 'member_id'); } /** * Route notifications for the mail channel. * * @param \Illuminate\Notifications\Notification $notification * @return array|string */ public function routeNotificationForMail($notification) { return $this->member->email; } }
OrderController的update方法
/** * Update the specified resource in storage. * * @param \Domain\Webshop\Http\Requests\Order\UpdateOrderRequest $request * @param string $id * @return \Illuminate\Http\Response */ public function update(UpdateOrderRequest $request, string $id) { $order_id = $this->repository->update($request, $id); $order = Order::whereId($order_id)->first(); $this->service->sendOrderUpdatedNotification($order); return response()->json([ 'id' => $order_id ]); }
OrderService代码
public function sendOrderUpdatedNotification(Order $order): void { if (is_null($order)) { return; }; $this->order = $order; $toMember = $this->getToMemberEmail(); $subject = OrderMailDefault::UPDATED_SUBJECT; // send the notification $this->order->notify(new OrderUpdatedNotification($toMember, $subject, $order)); }
OrderUpdatedNotification代码
class OrderUpdatedNotification extends Notification implements ShouldQueue { use Queueable; public string $subject; public string $email; public Order $order; public function __construct(string $to = null, string $subject = '', Order $order = null) { if (is_null($to)) { return; } if (is_null($order)) { return; } $this->subject = $subject ?? ''; $this->email = $to; $this->order = $order; } public function via(mixed $notifiable): array { return ['mail']; } public function viaQueues(): array { return [ 'mail' => QueueEnum::EMAIL, ]; } public function toMail(mixed $notifiable): OrderUpdatedMail { return (new OrderUpdatedMail($this->order)) ->to($this->email) ->subject($this->subject); } }
测试代码
class OrderControllerRegisteredUserTest extends BaseTestCase { protected function setUp(): void { parent::setUp(); $this->model = Order::class; $this->controller = OrderController::class; Mail::fake(); } public function create(int $count): void { $this->model::factory() // ... ->forMember() // ... ->create(); } /** @test */ public function shouldUpdateAsAuthorizedUser(): void { $this->create(1); $item = $this->model::first(); $item->name = $item->name . ' Changed'; $data = $item->toArray(); $this->put(action([$this->controller, 'update'], $item->id), $data) ->assertOk(); Notification::fake(); // Assert the notification was sent... Notification::assertSentTo( [$item->member], OrderUpdatedNotification::class, ); } }
测试结果
ParaTest v6.6.5 upon PHPUnit 9.5.26 by Sebastian Bergmann and contributors. F 1 / 1 (100%) Time: 00:01.354, Memory: 10.00 MB There was 1 failure: 1) Domain\Webshop\Tests\Order\OrderControllerRegisteredUserTest::shouldUpdateAsAuthorizedUser The expected [Domain\Webshop\Notifications\OrderUpdatedNotification] notification was not sent. Failed asserting that false is true. /var/www/html/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/NotificationFake.php:83 /var/www/html/vendor/laravel/framework/src/Illuminate/Support/Testing/Fakes/NotificationFake.php:67 /var/www/html/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:338 /var/www/html/src/Domain/Webshop/Tests/Order/OrderControllerRegisteredUserTest.php:100
问题原因及修复方案
1. Notification::fake()调用时机错误
测试中Notification::fake()是在发送请求之后调用的,此时通知已经发送完毕,Fake未生效。必须在发送请求之前启用Notification Fake。
2. 断言目标错误
代码中调用$order->notify(),通知的接收者是Order模型实例(因为它使用了Notifiable trait),而非$item->member。因此assertSentTo的第一个参数应为[$item]。
3. 队列通知同步处理
由于OrderUpdatedNotification实现了ShouldQueue,默认队列驱动下通知会进入队列延迟执行,导致断言失败。需在测试中启用同步队列。
修复后的测试代码
/** @test */ public function shouldUpdateAsAuthorizedUser(): void { $this->create(1); $item = $this->model::first(); $item->name = $item->name . ' Changed'; $data = $item->toArray(); // 提前启用Notification Fake Notification::fake(); // 启用同步队列,确保队列通知立即执行 $this->app['config']->set('queue.default', 'sync'); $this->put(action([$this->controller, 'update'], $item->id), $data) ->assertOk(); // 断言通知发送给Order实例 Notification::assertSentTo( [$item], OrderUpdatedNotification::class, ); }
额外检查点
- 确认
OrderService中的getToMemberEmail()方法正确返回会员邮箱,避免因$toMember为null导致Notification构造函数提前返回,未初始化属性。
内容的提问来源于stack exchange,提问作者Ferenc Bablena
相关产品推荐
相关产品推荐

