Faking the Queue in Laravel Tests

under Laravel

I'm working on a project where we use observers to dispatch a chain of jobs whenever a Volume model is created. While this is great when working on the code base, it can make it a bit difficult to write tests. For example, I want to test one of the jobs in the chain that is dispatched. Specifically, I want to make sure that the job fails when it should, and with the correct message.

So here's what I need the test to do:

  • Fake the JobFailed event so we can assert it was dispatched correctly
  • Create our model, but while the queue is faked, so the chain of jobs dispatched by the observer will not run
  • Dispatch the job we want to test
  • Assert the JobFailed event was fired

Here's what the test looked like originally:

1<?php
2 
3/** @test */
4public function it_fails_attach_volume_to_instance_if_volume_is_not_in_provisioning_state() {
5 Event::fake([JobFailed::class]);
6 
7 $queue = Queue::getFacadeRoot();
8 Queue::fake();
9 
10 $volume = VolumeFactory::make()->pending()->create();
11 
12 Queue::swap($queue);
13 
14 AttachVolumeToInstance::dispatch($volume);
15 
16 Event::assertDispatched(JobFailed::class, function ($event) {
17 return $event->exception->getMessage() === 'Volume is not in Provisioning state.';
18 });
19}

I didn't like the idea of having to store the $queue facade root and then having to replace it after. So I wrapped that up into a package:

1<?php
2 
3/** @test */
4public function it_fails_attach_volume_to_instance_if_volume_is_not_in_provisioning_state()
5{
6 // Given
7 Event::fake([JobFailed::class]);
8 
9 $volume = null;
10 QueueFake::wrap(function () use (&$volume) {
11 $volume = VolumeFactory::make()->pending()->create();
12 });
13 
14 // When
15 AttachVolumeToInstance::dispatch($volume);
16 
17 // Then
18 Event::assertDispatched(JobFailed::class, function ($event) {
19 return $event->exception->getMessage() === 'Volume is not in Provisioning state.';
20 });
21}

Now anytime we need to fake the queue for just a couple of lines, I can use QueueFake::wrap(). The queue will be faked for the duration of the closure.

Check it out on GitHub: https://github.com/ohseesoftware/laravel-queue-fake


Thanks for reading this article!

Hopefully you found this article useful! If you did, share it on Twitter!

Found an issue with the article? Submit your edits against the repository.