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

haakym's avatar

How to make assertions on a Laravel Mailable

In a test I would like to make some assertions on a Mailable using Mail::assertSent(), like this:

Mail::assertSent(MyMailable::class, function ($mail) use ($user) {
    return $mail->hasTo($user->email);
});

So far, I have found the hasTo(), hasFrom(), hasBcc() and all the other has*() methods work just great. However, when asserting a particular attribute on the Mailable exists, for example subject the attribute shows up as null and the assertion fails:

Mail::assertSent(MyMailable::class, function ($mail) {
    return $mail->subject === 'My Subject';
});

I believe this is because I have configured all of the Mailable attributes within the build() method which at the stage of the assertion probably hasn't been invoked, so the attributes are not set on the object yet.

I thought using the build() method was the correct approach to take based on the docs:

All of a mailable class' configuration is done in the build method. Within this method, you may call various methods such as from, subject, view, and attach to configure the email's presentation and delivery.

https://laravel.com/docs/5.5/mail#writing-mailables

I have found that I can get assertions on the Mailable's attributes working when I instead set the attributes on the constructor:

class MyMail extends Mailable
{
    public function __construct()
    {
        $this->subject = 'My Subject';
    }

    public function build() {
        return $this->subject('My Subject')->view('emails/my-email')
    }
}

However, I feel this approach is wrong because I feel like I am changing my code to suit my tests.

So, I would like to know if there is a better approach to making assertions against attributes on a Mailable? Any help would be most appreciated, thank you!

Here's the code of my actual test:

/** @test */
function a_notification_is_sent_when_an_application_is_updated()
{
    Mail::fake([RequiresVerification::class]);

    // some set up and factory methods called here...
    
    // the listener for this event sends mail
    ApplicationUpdated::dispatch($application);

    // this assertion passes
    Mail::assertSent(RequiresVerification::class);

    // this assertion does not pass when subject is set on the build()  
    // method but passes when subject is set on the constructor
    Mail::assertSent(RequiresVerification::class, function ($mail) use ($user) {
        return $mail->subject === 'hello';
    });
}
0 likes
4 replies
ohffs's avatar
ohffs
Best Answer
Level 50

Inside the assertSent block, if you do a $mail->build(); before doing your checks - does that work?

7 likes
haakym's avatar

@ohffs

Yes, that has done it. Maaaan I feel dumb that was an easy one!

Thank a lot for your help!

ohffs's avatar

@haakym ah - don't worry about it :-) just glad to help - I had to do exactly the same thing recently so it was in my head :-)

1 like

Please or to participate in this conversation.