Testing a Digest - Email Content

Published 1 week ago by thc1967

Hi again.

Today, I'm writing code that sends an email digest. To stay with the ubiquitous forum example everyone is familiar with, let's say that Users can Subscribe to get Notifications when Posts or Comments are created. Let's also say that we want to do this in daily digest format instead of immediately upon creation.

This means that I have to periodically create email and when I do that, I need to ensure that the Posts and Comments of interest to the User are included in the email while those that are not of interest to the user are excluded.

I already know how to code the email generation. I can generate the mail digest no problem.

What I don't know is how to write the unit test to dig into the body of the email to ensure that I see the information I expect to see and do not see the information I expect to be absent.

I can do something like this to verify that we send to the user I expect, but how do I interrogate the body of the email?

public function digest_email_filters_to_subscribed_posts()
{
    // Do my setup

    Mail::fake();

    // Do the thing that would send the emails

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

I think Tighten.co has a package that might let me test this, but I wanted to stick with core Laravel if it's possible there. (Edit: MailThief doesn't support mailables so I'd have to change implementation code, which I avoid doing if it's only because of testing.)

Thanks!

Best Answer (As Selected By thc1967)
druc

Hello

I assume you have some sort of collection when creating that PostDigest class, right?

For example, in one of my apps I use an event whenever somebody registers and then I send a welcome mail to that user. Something like:

Mail::to($this->user)->send(new WelcomeEmail($this->user));

I assume you'd have something like

Mail::to($this->user)->send(new PostDigest($digestCollectionWithStuff, $otherStuff));

You could test the contents of it in the callback. Something like this:

public function digest_email_filters_to_subscribed_posts()
{
    // Do my setup

    Mail::fake();
    $expectedItem = 'something';

    // Do the thing that would send the emails

    Mail::assertSent(PostDigest::class, function ($mail) use ($user, $expectedItem, $unexpectedItem) {

    $this->assertTrue($mail->items->contains($expectedItem));
    $this->assertFalse($mail->items->contains($unexpectedItem));

        return $mail->hasTo($user->email);
    }
}

Even so, I'd rather split the mailing functionality from the stuff that creates my digest and test those separately.

I'd have some DigestCreator class, pass a user, do whatever I need to do and test a getDigest() method.

This way I'm sure that i send the right digest info.

druc
druc
2 days ago (1,100 XP)

Hello

I assume you have some sort of collection when creating that PostDigest class, right?

For example, in one of my apps I use an event whenever somebody registers and then I send a welcome mail to that user. Something like:

Mail::to($this->user)->send(new WelcomeEmail($this->user));

I assume you'd have something like

Mail::to($this->user)->send(new PostDigest($digestCollectionWithStuff, $otherStuff));

You could test the contents of it in the callback. Something like this:

public function digest_email_filters_to_subscribed_posts()
{
    // Do my setup

    Mail::fake();
    $expectedItem = 'something';

    // Do the thing that would send the emails

    Mail::assertSent(PostDigest::class, function ($mail) use ($user, $expectedItem, $unexpectedItem) {

    $this->assertTrue($mail->items->contains($expectedItem));
    $this->assertFalse($mail->items->contains($unexpectedItem));

        return $mail->hasTo($user->email);
    }
}

Even so, I'd rather split the mailing functionality from the stuff that creates my digest and test those separately.

I'd have some DigestCreator class, pass a user, do whatever I need to do and test a getDigest() method.

This way I'm sure that i send the right digest info.

thc1967

I think I clicked too quickly. Sigh.

So I like the idea of having a separate digest creator. In fact, I think that's what I must do. $mail->items is nothing. I can see the things that I attach to the Mailable if I dump() it, but they wind up as protected properties so I can't look at them in the test.

Please sign in or create an account to participate in this conversation.