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

如何在测试中模拟Laravel的JobQueued队列事件?

问题描述

我需要测试一个包中监听Laravel内置队列事件(Illuminate\Queue\Events命名空间下所有事件)的监听器。

平时测试事件监听器行为我会按以下步骤:

  • 创建监听器要处理的事件
  • 实例化监听器并调用handle方法
  • 断言操作是否正确执行

示例代码:

public function test_sends_notification_to_newly_created_user(): void
{
    $user = User::factory()->create();
    $event = new UserCreated($user);

    $eventListener = $this->app->make(SendUserCreationNotification::class);
    $eventListener->handle($event);

    Notification::assertSentTo($user, WelcomeNotification::class);
}

但针对JobQueued事件的监听器,我遇到了问题:

我的监听器代码:

public function handle(JobQueued $event): void
{
    $jobUuid = $event->payload()['uuid'];

    if (property_exists($event->job, 'trackableJob')) {
        $this->updater->setQueuedStatus($event->job->trackableJob, $jobUuid);
    }
}

尝试创建JobQueued事件时,不想手动构造复杂的payload参数:

public function test_updates_status_to_queued_in_database(): void
{
    $event = new JobQueued(...); // 不想手动指定payload
}

也试过直接调度任务并运行队列,但JobQueued事件从未触发:

public function test_updates_status_to_queued_in_database(): void
{
    app(Dispatcher::class)->dispatch(new TrackedJob());
    
    $this->artisan('queue:work --once');
}

补充:updater类的setQueuedStatus方法用于更新任务状态为已排队并关联UUID:

public function setQueuedStatus(TrackableJob $trackableJob, string $jobUuid): bool
{
    $trackableJob->job_uuid = $jobUuid;

    return $trackableJob->update([
        'queued_at' => now(),
        'status' => Status::Queued,
    ]);
}

TrackedJob是一个handle方法为空的测试任务类。

请问如何正确创建JobQueued事件来测试我的监听器?


解决方案

方案一:模拟JobQueued事件(单元测试推荐)

不用手动构建完整的JobQueued实例,利用Mockery模拟监听器需要的属性和方法即可:

public function test_updates_status_to_queued_in_database(): void
{
    // 1. 创建测试用的TrackableJob模型
    $trackableJob = TrackableJob::factory()->create();

    // 2. 模拟Job实例,添加trackableJob属性
    $mockJob = Mockery::mock(\Illuminate\Contracts\Queue\Job::class);
    $mockJob->trackableJob = $trackableJob;

    // 3. 模拟JobQueued事件,返回包含uuid的payload
    $mockEvent = Mockery::mock(\Illuminate\Queue\Events\JobQueued::class);
    $mockEvent->job = $mockJob;
    $mockEvent->shouldReceive('payload')
              ->andReturn(['uuid' => 'test-uuid-123']);

    // 4. 获取监听器实例并调用handle方法
    $listener = $this->app->make(YourJobQueuedListener::class);
    $listener->handle($mockEvent);

    // 5. 断言状态更新正确
    $trackableJob->refresh();
    $this->assertEquals('test-uuid-123', $trackableJob->job_uuid);
    $this->assertNotNull($trackableJob->queued_at);
    $this->assertEquals(Status::Queued, $trackableJob->status);
}

方案二:让JobQueued事件真实触发(集成测试)

如果需要测试完整调度流程,调整队列配置并确保事件分发正常:

  1. 修改测试环境队列驱动:在phpunit.xml中设置队列驱动为sync,任务会立即执行并触发队列事件:
<env name="QUEUE_CONNECTION" value="sync"/>
  1. 确保监听器已注册:如果包的服务提供者未自动注册监听器,可在测试中临时注册:
protected function setUp(): void
{
    parent::setUp();

    $this->app['events']->listen(
        \Illuminate\Queue\Events\JobQueued::class,
        YourJobQueuedListener::class
    );
}
  1. 编写测试代码:
public function test_updates_status_to_queued_in_database(): void
{
    // 创建TrackableJob并关联到测试任务
    $trackableJob = TrackableJob::factory()->create();
    $job = new TrackedJob();
    $job->trackableJob = $trackableJob;

    // 调度任务,sync驱动下会触发JobQueued事件
    dispatch($job);

    // 断言状态更新正确
    $trackableJob->refresh();
    $this->assertNotNull($trackableJob->job_uuid);
    $this->assertNotNull($trackableJob->queued_at);
    $this->assertEquals(Status::Queued, $trackableJob->status);
}

注意:sync驱动会让任务立即执行,同时按顺序触发JobQueued、JobProcessing等队列事件,适合测试完整流程。

内容的提问来源于stack exchange,提问作者user25728292

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 19:20:06