My Laravel 5.5 application has a Product
model. The Product
model has a dispatchesEvents
property that looks like this:
/**
* The event map for the model.
*
* @var array
*/
protected $dispatchesEvents = [
'created' => ProductCreated::class,
'updated' => ProductUpdated::class,
'deleted' => ProductDeleted::class
];
I also have a listener that is called CreateProductInMagento
which is mapped to the ProductCreated
event in the EventServiceProvider
. This listener implements the ShouldQueue
interface.
When a product is created, the ProductCreated
event is fired and the CreateProductInMagento
listener is pushed to the queue and is run.
I am now trying to write a test for all of this. Here is what I have:
/** @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);
}
But I get a The expected [AppListenersMagentoProductCreateProductInMagento] job was not pushed.
error message.
How do I test queueable listeners using Laravel’s Queue::fake()
method?
2
Answers
Running
artisan queue:work
won’t solve the issue because when testing, Laravel is configured to use thesync
driver, which just runs jobs synchronously in your tests. I am not sure why the job is not being pushed, though I would guess it has to do with Laravel handling events differently in tests. Regardless, there is a better approach you can take to writing your tests that should both fix the issue and make your code more extendable.In your
ProductTest
, rather than testing thata_created_product_is_pushed_to_the_queue_so_it_can_be_added_to_magento
, you should simply test that the event is fired. YourProductTest
doesn’t care what theProductCreated
event is; that is the job of aProductCreatedTest
. So, you can use Event Faking to change your test a bit:Then, create a new
ProductCreatedTest
to unit test yourProductCreated
event. This is where you should place the assertion that a job is pushed to the queue:This has the added benefit of making your code easier to change in the future, as your tests now more closely follow the practice of only testing the class they are responsible for. Additionally, it should solve the issue you’re having where the events fired from a model aren’t queuing up your jobs.
The problem here is that the listener is not the job pushed to the queue. Instead, there’s a
IlluminateEventsCallQueuedListener
job that is queued and will in turn call the appropriate listener when resolved.So you could do your assertion like this: