我的Laravel 5.5应用程序具有Product
模型。 Product
模型具有dispatchesEvents
属性,如下所示:
/**
* The event map for the model.
*
* @var array
*/
protected $dispatchesEvents = [
'created' => ProductCreated::class,
'updated' => ProductUpdated::class,
'deleted' => ProductDeleted::class
];
我还有一个名为CreateProductInMagento
的侦听器,它被映射到ProductCreated
中的EventServiceProvider
事件。该侦听器实现ShouldQueue
接口。
创建产品时,会触发ProductCreated
事件并将CreateProductInMagento
侦听器推送到队列并运行。
我现在正在尝试为所有这些编写测试。这就是我所拥有的:
/** @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()
方法测试可排队的侦听器?
答案 0 :(得分:9)
这里的问题是侦听器不是推送到队列的作业。相反,有一个Illuminate\Events\CallQueuedListener
作业排队,并在解决后依次调用适当的侦听器。
所以你可以这样做你的断言:
Queue::assertPushed(CallQueuedListener::class, function ($job) {
return $job->class == CreateProductInMagento::class;
});
答案 1 :(得分:1)
运行artisan queue:work
无法解决问题,因为在测试时,Laravel配置为使用sync
驱动程序,该驱动程序只在测试中同步运行作业。我不确定为什么这项工作没有被推动,尽管我猜它与Laravel在测试中处理事件的方式有所不同。无论如何,您可以采用更好的方法编写测试,既可以解决问题,也可以使代码更具扩展性。
在ProductTest
中,您应该只测试该事件是否被触发,而不是测试a_created_product_is_pushed_to_the_queue_so_it_can_be_added_to_magento
。您的ProductTest
并不关心ProductCreated
事件是什么;这是ProductCreatedTest
的工作。因此,您可以使用Event Faking稍微更改您的测试:
/** @test */
public function product_created_event_is_fired_upon_creation()
{
Event::fake();
factory(Product::class)->create();
Event::assertDispatched(ProductCreated::class);
}
然后,创建一个新的ProductCreatedTest
以对您的ProductCreated
事件进行单元测试。这是您应该将作业推送到队列的断言:
/** @test */
public function create_product_in_magento_job_is_pushed()
{
Queue::fake();
// Code to create/handle event.
Queue::assertPushed(CreateProductInMagento::class);
}
这样做的另一个好处是可以让您的代码在将来更容易更改,因为您的测试现在更加严格地遵循仅测试他们负责的类的做法。此外,它应该解决您从模型触发的事件不会排队工作的问题。