You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 03:41:29