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

Rankus's avatar

Is Mail:fake() and asserts really working ?

Hello,

I'm trying to test email sendings with the native officiel faker Mail::fake() but I can't have it work.

My email is a classical Mailable which is not queued :

class UserAlsoCommentedMail extends Mailable
{
    use SendinBlue;

    public function __construct(private UserAlsoCommentedNotification $notification)
    {
        // Do some stuff....
    }

    public function build()
    {
        $subject = "My subject";

        return $this
            ->subject($subject)
            ->view(
                'mail.user-also-commented',
            )
            ->text(
                'mail.user-also-commented-text',
            )
            ->sendinblue(
                [
                    'tags' => [
                        'tag1',
                        'tag2',
                    ],
                ]
            );
    }
}

My test :

public function testPostCreateEvent() 
{
    Mail::fake();

    //Given
    $userOwner = User::factory()->createOne();
    $userComment = User::factory()->createOne();

    /** @var Publication $publication */
    $publication = Publication::factory()->createOne([
        'user_id'=> $userOwner->id
    ]);

    //When
    $response = $this
        ->actingAs($userComment, 'user')
        ->json('POST', "/api/v1/my-endpoint/comments", [
            'description' => 'a new comment',
        ]);

    //Then
    $response->assertSuccessful();
    $publication->refresh();

    Mail::assertSent(CommentCreatedMail::class);
    Mail::assertSent(UserAlsoCommentedMail::class);
}

My emails are instanciated and built, so is the MailFake but the Mail::assertSent are always false.

Digging into Laravel, I landed onto MailFake::send function which returns too early in my case :

/**
 * Send a new message using a view.
 *
 * @param  \Illuminate\Contracts\Mail\Mailable|string|array  $view
 * @param  array  $data
 * @param  \Closure|string|null  $callback
 * @return void
 */
public function send($view, array $data = [], $callback = null)
{
    if (! $view instanceof Mailable) {
        return; // I return here as my views are just array and not Mailable
    }

    $view->mailer($this->currentMailer);

    $this->currentMailer = null;

    if ($view instanceof ShouldQueue) {
        return $this->queue($view, $data);
    }

    $this->mailables[] = $view;
}

In my case, I return from the first condition as my views are just array and not Mailable.

I think there is someting wrong here in Laravel MailFake as email views cannot be Mailable but arrays of Blade views.

What do you think about that ? Do your Mail::assertSent work ?

0 likes
4 replies
Sinnbeck's avatar

Can you show the code for sending the email?

Rankus's avatar

Emails are sent through a notification :

class CommentCreatedNotification extends Notification
{
    public function via(): array
    {
        return [
            'mail',
        ];
    }

    public function toMail(User $notifiable): Mailable
    {
        return (new CommentCreatedMail($this))
            ->to($notifiable->email);
    }
}
Rankus's avatar

Also,

my testing env config :

MAIL_MAILER=array
<server name="MAIL_MAILER" value="array"/>
Rankus's avatar
Rankus
OP
Best Answer
Level 2

Hello,

at last, I went through that problem by stopping at the notification level as the Mail::fake() seems to be made for "facade" sent emails and not "notifications" sent emails.

Stopping at notification level, you then have to prepare the email by yourself :

public function testSendValidNotification()
{
    Notification::fake();

    //Given
    ...

    ///When
    ...

    //Then
    Notification::assertSentTo(
        $user,
        ValidNotification::class,
        function (ValidNotification $n, $channels) use ($content, $user) {
            $mail = $n->toMail($user)->build();
            $mail->assertSeeInHtml("Validated !");
            $mail->assertSeeInText("Validated !");
            return
                in_array('mail', $channels) &&
                $n->content->is($content) &&
                $mail->hasTo($user->email) &&
                $n->notifiable->id === $user->id;
        }
    );
}

Illuminate\Mail\Mailable class gives you some useful assert functions :

public function assertSeeInHtml($string)
public function assertDontSeeInHtml($string)
public function assertSeeInText($string)
public function assertDontSeeInText($string)
2 likes

Please or to participate in this conversation.