使用 Queue::fake() 测试侦听器

2022-08-30 19:10:55

我的Laravel 5.5应用程序有一个模型。该模型具有如下所示的属性:ProductProductdispatchesEvents

/**
 * The event map for the model.
 *
 * @var array
 */
protected $dispatchesEvents = [
    'created' => ProductCreated::class,
    'updated' => ProductUpdated::class,
    'deleted' => ProductDeleted::class
];

我还有一个名为的侦听器,该侦听器映射到 .此侦听器实现接口。CreateProductInMagentoProductCreatedEventServiceProviderShouldQueue

创建产品时,将触发事件,并将侦听器推送到队列并运行。ProductCreatedCreateProductInMagento

我现在正试图为所有这些写一个测试。这是我所拥有的:

/** @test */
public function a_created_product_is_pushed_to_the_queue_so_it_can_be_added_to_magento()
{
    Queue::fake();

    factory(Product::class)->create();

    Queue::assertPushed(CreateProductInMagento::class);
}

但是我收到一条错误消息。The expected [App\Listeners\Magento\Product\CreateProductInMagento] job was not pushed.

如何使用Laravel的Queue::fake()方法测试可排队的侦听器?


答案 1

这里的问题是侦听器不是推送到队列的作业。相反,有一个作业正在排队,并在解析后依次调用相应的侦听器。Illuminate\Events\CallQueuedListener

所以你可以像这样做你的断言:

Queue::assertPushed(CallQueuedListener::class, function ($job) {
    return $job->class == CreateProductInMagento::class;
});

答案 2

运行不会解决问题,因为在测试时,Laravel 配置为使用驱动程序,该驱动程序只是在测试中同步运行作业。我不确定为什么这项工作没有被推动,尽管我猜它与Laravel在测试中以不同的方式处理事件有关。无论如何,您可以采用更好的方法来编写测试,该方法既可以解决问题,又可以使代码更具可扩展性。artisan queue:worksync

在 中,您应该简单地测试该事件是否被触发,而不是测试该事件。你不在乎事件是什么;这是.因此,您可以使用事件伪造来稍微更改测试:ProductTesta_created_product_is_pushed_to_the_queue_so_it_can_be_added_to_magentoProductTestProductCreatedProductCreatedTest

/** @test */
public function product_created_event_is_fired_upon_creation()
{
    Event::fake();

    factory(Product::class)->create();

    Event::assertDispatched(ProductCreated::class);
}

然后,创建一个新的单元测试事件。这是您应该放置将作业推送到队列的断言的位置:ProductCreatedTestProductCreated

/** @test */
public function create_product_in_magento_job_is_pushed()
{
    Queue::fake();

    // Code to create/handle event.

    Queue::assertPushed(CreateProductInMagento::class);
}

这具有使代码将来更容易更改的额外好处,因为您的测试现在更紧密地遵循仅测试它们负责的类的做法。此外,它应该可以解决您遇到的问题,即从模型触发的事件不会使您的作业排队。


推荐