Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

bwrigley's avatar

How to test mail sent from Notification

My users can specify if they have Notifications sent to them instantly or to be notified daily of any pending Notifications.

This all works fine with manual testing, so now I'd like to write a feature test around this. This is what I have so far:

    public function test_instant_notification_is_sent(): void
    {
        Mail::fake();

        $this->user = $user = factory(User::class)->create();

        $this->user->update(
            [
                'notification_frequency' => 'instant',
                'notification_method' => 'email',
                'notification_email' => $this->email,
            ]
        );

        $this->user->save();

        $email = ['subject' => 'subject', 'data' => 'data'];
        $sms = 'Test Notification';
        $database = ['some' => 'data'];

        $this->user->notify(new TestNotification($email, $sms, $database));

        Mail::assertSent(MailMessage::class);

and I've written the TestNotification to go with it:

class TestNotification extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     *
     * @param string[] $email
     * @param string $sms
     * @param string[] $database
     * @return void
     */
    public function __construct(array $email, string $sms, array $database)
    {
        $this->email = $email;
        $this->sms = $sms;
        $this->database = $database;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return string[]
     */
    public function via($notifiable): array
    {
        return [$notifiable->preferredNotificationMethod()];
    }

    /**
     * @param  mixed  $notifiable
     * @return string[]
     */
    public function toDatabase($notifiable): array
    {
        return $this->database;
    }

    /**
     * @param  mixed  $notifiable
     * @return string[]
     */
    public function toNexmo($notifiable): array
    {
        return (new NexmoMessage)
            ->content($this->sms);
    }

    /**
     * @param  mixed  $notifiable
     * @return string[]
     */
    public function toMail($notifiable): MailMessage
    {
        return (new MailMessage)
            ->greeting($this->email['subject'])
            ->line('Testing Notifications!')
            ->line('Thank you for using our application!');
    }
}

So my test fails because, as I now see, MailMessage is not a mailable and I think Mail::fake() and the mail assertions only work with mailables.

Incidentally, if I remove Mail::fake() the email is sent fine.

How have others managed to test this please without actually sending the email.

0 likes
4 replies
bwrigley's avatar

@nash

Thanks for your reply, and sorry I wasn't clear. I specifically want to test the mail channel and whilst Notification::fake() is great for testing Notifications, Notification::assertSentTo() is channel agnostic.

So if the user's preferred channel is nexmo then Notification::assertSentTo() will be true or if the channel is database then Notification::assertSentTo() is also true.

I specifically want to make sure that I am routing instant notifications via the mail channel if that's the channel the user has accepted, so I need to test if an email has been sent.

I hope that makes sense.

Nash's avatar
Nash
Best Answer
Level 20

@bwrigley You could test that the channel is set correctly, e.g. $this->assertEquals(['mail'], $notification->via($request->user())), or however you are specifying the preferred channel. There is no point in testing the framework itself since that has already been tested. If Laravel is set to send an email you should trust that it will do so.

1 like
bwrigley's avatar

@nash that's the one brilliant thank you! Yes I wasn't wanting to test the actual mail was sent but couldn't figure out how to do what you have just suggested.

Thanks again!

Please or to participate in this conversation.