A different way to test your Jobs
UPDATE 2018-06-27: I learned a lot about queues and testing since I wrote this post. I no longer test my jobs or queues like I've described in here, but it has some interesting points that prevent me for deleting this article. If you want to test your jobs this way, of course you can, but let me tell you that there's a better and easier way to do this
I recently entered the huge world of executing Laravel jobs in background with Queues.
It was confusing initially because I had no experience on this topic and there was a lot of information on the internet and I didn't know where to start.
So, one step at a time.
The first big problem I faced was
How can I test that an API call really sends the job it creates to a queue?
I solved this problem was to switch the queue from redis to the database driver in your's .env file
QUEUE_DRIVER=database
After that, you can obtain that table with the following command:
php artisan queue:table
(Probably you will need the table for failed_jobs too)
php artisan queue:failed-table
Testing
In your tests, you can check the contents of the table:
/** @test */
public function register_user_call_created_send_email_job()
{
$response = $this->json('POST', '/api/user/register', [
'name' => 'John',
'surname' => 'Doe',
'email' => 'john@doe.com'
];
$response->assertStatus(201);
$this->assertDatabaseHas('jobs', [
'queue' => 'register-emails'
]);
}
After this, I wanted to test the contents of the job, and the Job methods themselves. I created a model Job
and added the following code to it:
class Job extends Model
{
// This will store the unserialized command, acting like a Singleton
protected $unserializedCommand;
public function getNameAttribute()
{
return $this->payload->displayName;
}
// Decode the content of the payload since it's stored as JSON
public function getPayloadAttribute()
{
return json_decode($this->attributes['payload']);
}
// Since Laravel stores the command serialized in database, this undo that serialization
// and save the result into the singleton property to prevent unserializing again
// (since it's a hard task)
public function getCommandAttribute()
{
if (! $this->unserializedCommand) {
$this->unserializedCommand = unserialize($this->payload->data->command);
}
return $this->unserializedCommand;
}
}
Accessors are a really powerful Laravel feature, one of the most I love. You can use them to transform data, create new data on the fly, or even get fields from a database you know that needs to be refactorized
Using this new model, I'm able to test the content of the Job like this:
/** @test */
public function register_mail_job_receives_the_correct_email()
{
$response = $this->json('POST', '/api/user/register', [
'name' => 'John',
'surname' => 'Doe',
'email' => 'john@doe.com'
];
$response->assertStatus(201);
$job = App\Models\Job::first();
$this->assertEquals('john@doe.com', $job->command->email);
}
You'll need to make sure that your job properties are public in order to check them, or create getters instead.
Tinkering
If you're not using tests but still wants to see what's inside your job, you can use Laravel's awesome tinker
command like this:
$ php artisan tinker
> $job = App\Models\Job::first();
> $job->command->email;
'john@doe.com'
>
Back to production
Once you have finished debugging and testing your jobs, you can switch back to redis by changing your env's variable QUEUE_DRIVER
to redis
.
This will result in another problem: your tests will stop working.
To fix this, you can change your environment variable in your phpunit.xml
file:
<env name="QUEUE_DRIVER" value="database"/>
You may also like:
Found a typo?
Edit this post.
Special thanks to Dieter for
the amazing code of this blog.