Bump for the day crowd
Help testing events and mail
My goal is to create a table that contains a list of email address that we will never send email to. I found a guide on Ralph J Smit's site that by description seems to allow me to do exactly that.
I've created a listener:
<?php
namespace App\Listeners;
use Arr;
use App\Models\DoNotEmail;
use Illuminate\Mail\Events\MessageSending;
use Illuminate\Notifications\Events\NotificationSending;
class FilterEmailsSentToDoNotEmailAddressesListener
{
/**
* Handle the event.
*/
public function handle(MessageSending|NotificationSending $event): null|bool
{
if ($event instanceof NotificationSending && !in_array('mail', $event->notification->via($event->notifiable))) {
return null;
}
// Get the email address as a string
$email = match ($event::class) {
NotificationSending::class => $event->notifiable->routeNotificationFor('mail'),
MessageSending::class => Arr::first($event->message->getTo())?->getAddress()
};
if (!$this->shouldSendEmail($email)) {
return false;
}
return null;
}
protected function shouldSendEmail(string $address): bool
{
$found = DoNotEmail::where('address', $address)->get();
if ($found) {
return false;
}
return true;
}
}
I've registered that listener in the EventServiceProvider:
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
MessageSending::class => [
FilterEmailsSentToDoNotEmailAddressesListener::class,
],
NotificationSending::class => [
FilterEmailsSentToDoNotEmailAddressesListener::class,
]
];
But my problem is testing that it actually works.
If I try using Mail::fake(); in my test, like so:
#[Test]
public function if_we_attempt_to_email_an_address_in_the_do_not_email_list_it_will_not_be_sent(): void
{
$address = '[email protected]';
DoNotEmail::factory()->create(['address' => $address]);
$league = League::factory()->create();
Mail::fake();
Mail::to($address)->send(new NewSeasonNotification($league));
Mail::assertNothingSent();
}
The result is:
The following mailables were sent unexpectedly: App\Mail\NewSeasonNotification
So I was thinking, "OK, maybe when you fake the Mail facade, the MessageSending / MessageSent events don't actually get triggered and so the event listener doesn't actually get a chance to intercept the call. What if instead of faking mail, I instead Event::fake(); and then watch for those events, making sure that Sending is triggered (since that's the event that kicks off our listener), but since the listener kicks back a false (the email address is in the DoNotEmail::where.... lookup), the MessageSent event should never happen, like this:"
#[Test]
public function if_we_attempt_to_email_an_address_in_the_do_not_email_list_it_will_not_be_sent(): void
{
$address = '[email protected]';
DoNotEmail::factory()->create(['address' => $address]);
$league = League::factory()->create();
Event::fake();
Mail::to($address)->send(new NewSeasonNotification($league));
Event::assertDispatched(MessageSending::class); // This should be dispatched
Event::assertNotDispatched(MessageSent::class); // This shouldn't be
}
But then I get: The unexpected [Illuminate\Mail\Events\MessageSent] event was dispatched. and the email does end up showing in my inbox (in the actual test, I use my actual email address).
Can anyone shed some light into what might be happening and how I can better test this?
Thanks!
Please or to participate in this conversation.