Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing listeners with Queue::fake()

Tags:

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 [App\Listeners\Magento\Product\CreateProductInMagento] job was not pushed. error message.

How do I test queueable listeners using Laravel's Queue::fake() method?

like image 284
Denis Priebe Avatar asked Jan 03 '18 19:01

Denis Priebe


People also ask

How do you test event listeners?

You can test the event listener does its thing by instantiating the listener, then getting it to handle your event. // Create a listener instance. $listener = app()->make(YourListener::class); // or just app(YourListener::class) // Create an event instance.

What is queue listener?

A queue listener is an application that reads and processes these queued messages. Because the Service Bus messages are stored in a queue, a listener doesn't have to be actively listening for messages to be received in the queue.

What is mock in Laravel?

When testing Laravel applications, you may wish to "mock" certain aspects of your application so they are not actually executed during a given test. For example, when testing a controller that dispatches an event, you may wish to mock the event listeners so they are not actually executed during the test.

What is event listener in laravel?

Laravel's events provide a simple observer pattern implementation, allowing you to subscribe and listen for various events that occur within your application. Event classes are typically stored in the app/Events directory, while their listeners are stored in app/Listeners .


2 Answers

The problem here is that the listener is not the job pushed to the queue. Instead, there's a Illuminate\Events\CallQueuedListener job that is queued and will in turn call the appropriate listener when resolved.

So you could do your assertion like this:

Queue::assertPushed(CallQueuedListener::class, function ($job) {     return $job->class == CreateProductInMagento::class; }); 
like image 91
alepeino Avatar answered Oct 08 '22 05:10

alepeino


Running artisan queue:work won't solve the issue because when testing, Laravel is configured to use the sync 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 that a_created_product_is_pushed_to_the_queue_so_it_can_be_added_to_magento, you should simply test that the event is fired. Your ProductTest doesn't care what the ProductCreated event is; that is the job of a ProductCreatedTest. So, you can use Event Faking to change your test a bit:

/** @test */ public function product_created_event_is_fired_upon_creation() {     Event::fake();      factory(Product::class)->create();      Event::assertDispatched(ProductCreated::class); } 

Then, create a new ProductCreatedTest to unit test your ProductCreated event. This is where you should place the assertion that a job is pushed to the queue:

/** @test */ public function create_product_in_magento_job_is_pushed() {     Queue::fake();      // Code to create/handle event.      Queue::assertPushed(CreateProductInMagento::class); } 

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.

like image 28
samrap Avatar answered Oct 08 '22 07:10

samrap