I think that this one applies here: https://mauricius.dev/test-laravel-job-delayed-in-queue/
How to test emails being sent later
I have a simple mail code
Mail::to("[email protected]")->later(now()->addSeconds(5), $mailable);
How do I test that the email will be sent in 5 seconds ?
Note that regular tests work
Mail::assertQueued($mailable::class)
Reading the implementation of later in MailFake, i realize that the delay is being ignored, so I assume there's no proper way, but I still ask in case I'm missing something.
Thanks,
@Nakov Hey,
Yeah, found out this article before I decided to write here. It doesn't really help since I'm using the Mail facade to send my email. The later method being just a shell bypassing any queue logic, the advices given in the article are not applicable.
I think I will try to make a PR improving the later method of the MailFake implementation to have the method properly testable.
Thanks,
@beNjiox If you are queuing the emails this should work.
However I am looking forward to check out your PR, please share it here if you come up with something.
@Nakov If I use the Queue facade when sending the email, yes.
I will be able to query the $delay property of the job just like the articles says
Queue::assertPushed(Mailable::class, function ($job) {
return ! is_null($job->delay);
});
But here I'm using the Mail facade.
When testing using that, even when doing Mail::assertQueued, the callback gets the Mailable, not a Job, which does not expose publicly the underlying $delay property.
@beNjiox You don't have to use the Queue facade to send the email, you can still use the Mail facade, the Mail however should use the Queueable trait, that will give you access to the delay property :)
@Nakov Hey,
Thanks for taking the time. I just tested, with both the Queuable trait (which I already had) and even with the ShouldQueue interface implemented.
Both don't work.
Just so we are in the same page, here's my production code
Mail::to($email)->later(now()->addSeconds(5), $mailable);
Here's my test code
Mail::assertQueued(
OrderConfirmed::class,
function ($mail) use ($order) {
// dump($mail) shows a Mailable object ...
// ... and dump($mail->delay) shows null
return $mail->delay === 42;
}
);
Output :
The expected [App\Mail\OrderConfirmed] mailable was not queued.
Failed asserting that false is true.
Note that testing anything else than $mail->delay === 42 works, testing the email being queued is not a problem.
Am I missing something ? Is that something you've been able to do yourself in your projects ?
@beNjiox I hope I am of any help my friend, I just love diving deep into some problems that I might need at some point. I am using a test project myself to try out these things, so I don't talk in vain.. This helps me learn the framework more.
I assume that you've got a QUEUE_CONNECTION=sync for your tests, which will never set the delay property, if you open the Mailable class and you see the later implementation it checks the connection which in your case will be MailFake and the later() method in there never sets the value of the delay hence the reason why you get null.
I would love to see where you end up with this, and I might spent more time during the weekend to make a scenario on my app to test this. I hope you don't feel like I wasted your time, I am not playing smart ass, just trying to help :)
I experienced the same issue, and it seems that if the delay is set on the Mailable class instead of the Mail facade, the delay property is accessible during testing.
The code should be something like this:
$mailable = (new MailableClass($parameter))->delay(now()->addSeconds(5));
Mail::to($email)->queue($mailable);
Please or to participate in this conversation.