Search code examples
laravelphpunitlaravel-maillaravel-notification

How to test that a mailable was sent from a notification?


What's the secret of testing a mailable was sent from a notification?

Testing the notification:

it('does send notification when model is deleted', function() {
    Notification::fake();
    $this->model->delete();
    Notification::assertSentTo($this->model->user, MyModelDeletedNotification::class);
});

Passes.

Testing the mailable:

it('does send email when model is deleted', function() {
    Mail::fake();
    $this->model->delete();
    Mail::assertQueued(MyModelDeletedMail::class, 1);
});

Fails. No mails queued.

When a model is deleted a observer method is triggered:

public function deleted(MyModel $model)
{
    if ($model->isForceDeleting()) {
        return;
    }

    $model->user->notify(new MyModelDeletedNotification($model));
}

Notification:

class MyModelDeleted extends Notification implements ShouldQueue
{
    use Queueable;

    ...

    public function via($notifiable)
    {
        return ['mail', 'database'];
    }

    public function toMail($notifiable)
    {
        return (new MyModelDeletedMail($this->model))->to($notifiable->email);
    }

    ...
}

Mailable:

class ConsultationDeleted extends Mailable
{
    use Queueable, SerializesModels;

    ...

    public function build()
    {
        ...
    }
}

When I dump("foobar") inside the mailables constructor or build method, the message is appearing in the log. However the test fails. What am I missing here?


Solution

  • Regarding using Mail:fake in the context of notifications:

    It is not a very good way to test because it catches only mails sent using the Mail facade (doesn't intercept mails sent through a notification or using a mailer retrieved via dependency injection).

    Make sure you have set

    <server name="MAIL_MAILER" value="array"/>
    

    phpunit.xml. In your test:

    it('does send email', function() {
    
        // business logic
    
        $emails = app()->make('mailer')->getSwiftMailer()->getTransport()->messages();
    
        assertCount(1, $emails);
        assertEquals([$this->user->email], array_keys($emails[0]->getTo()));
    });
    

    This worked for me using laravel 8.

    Reference: https://laracasts.com/discuss/channels/testing/testing-if-email-was-sent-with-out-sending-it?page=1&replyId=402801