When it comes to Queue testing in Laravel, I use the provided Queue Fake functionality. However, there is a case where I need to create a Mock for a Job class.
I have the following code that pushes a job to a Redis powered queue:
$apiRequest->storeRequestedData($requestedData); // a db model
// try-catch block in case the Redis server is down
try {
App\Jobs\ProcessRunEndpoint::dispatch($apiRequest)->onQueue('run');
$apiRequest->markJobQueued();
} catch (\Exception $e) {
//handle the case when the job is not pushed to the queue
}
I need to be able to test the code in the catch block. Because of that, I'm trying to mock the Job object in order to be able to create a faker that will throw an exception.
I tried this in my Unit test:
ProcessRunEndpoint::shouldReceive('dispatch');
That code returns: Error: Call to undefined method App\Jobs\ProcessRunEndpoint::shouldReceive()
.
I also tried to swap the job instance with a mock object using $this->instance()
but it didn't work as well.
That said, how can I test the code in the catch block?
I found a solution. Instead of using a facade for adding a job to the queue (App\Jobs\ProcessRunEndpoint::dispatch($apiRequest)->onQueue('run');
), I injected it into the action of the controller:
public function store(ProcessRunEndpoint $processRunEndpoint)
{
// try-catch block in case the Redis server is down
try {
$processRunEndpoint::dispatch($apiRequest)->onQueue('run');
} catch (\Exception $e) {
//handle the case when the job is not pushed to the queue
}
}
With this, the job object is resolved from the container, so it can be mocked:
$this->mock(ProcessRunEndpoint::class, function ($mock) {
$mock->shouldReceive('dispatch')
->once()
->andThrow(new \Exception());
});
Although still not sure why the shouldReceive
approach doesn't work for the facade: https://laravel.com/docs/8.x/mocking#mocking-facades