★ Improving assertions on Laravel fakes
Tags: laravel, php
Published: 2020-01-02 23:30:15

---
title: ★ Improving assertions on Laravel fakes
published: true
date: 2019-06-24 08:10:15 UTC
tags: laravel, php
canonical_url: https://freek.dev/1390-improving-assertions-on-laravel-fakes
---

Laravel has some cool [fakes](https://laravel.com/docs/master/mocking#event-fake) that can help you with testing your code. In this short blog post, I'd like to show you a little tip on how to get better feedback when a test that contains such a fake fails.

<!--more-->
## How to use a fake #

Let's start with an example of a fake. You can skip to the next section if you've already worked with fakes.

Imagine your application sends a mail. You want to test if that mail is being sent correctly. The first step would be to set up the fake. Laravel makes this incredibly easy. Here's what you need to do:

```php
use Illuminate\Support\Facades\Mail;

// inside a PHPUnit test class

/** @test */
public function this_is_your_test() 
{
     Mail::fake();
}
```

That `fake` method will swap the real mailer (that Laravel already set up) in the IoC container by a fake one. That fake one will not send the mail.

With this in place, you can test if a mail has been sent. You have to pass [your mailable class](https://laravel.com/docs/master/mail#writing-mailables) to `assertSent`.

```php
use Illuminate\Support\Facades\Mail;

/** @test */
public function this_is_your_test() 
{
     Mail::assertSent(OrderShipped::class);
}
```

## Improving assertions on fakes #

That `assertSent` method accepts a callable as a second argument. That callable will receive the mailable. In that callable, you can use methods like `hasTo`, `hasCc` to assert that the mailable is configured correctly. The docs contain [the following example](https://laravel.com/docs/master/mocking#mail-fake):

```php
// Assert a message was sent to the given users...
 Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
     return $mail->hasTo($user->email) &&
            $mail->hasCc('...') &&
            $mail->hasBcc('...');
 });
```

Now, the test only passes when that callable returns `true`. Nice!

But what if for some reason that callable doesn't return `false`? The failed test will output a message, not unlike this one:

```
The expected [OrderShipped::class] mailable was not sent.
```

The problem here is that it's not clear if the `to`, `cc` or `bcc` is not correct. Let's improve that.

Probably you're using testing the mails inside of a PHPUnit test. Instead of just having one `return` statement in the callable, let's use PHPUnit assertions.

```php
Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
   $this->assertTrue($mail->hasTo($user->email));
   $this->assertTrue($mail->hasCc('...'));
   $this->assertTrue($mail->hasBcc('...'));

   return true;
});
```

Sure, it's a bit more code to type. But with these assertions in place, a failing test will now also report the line number of the failed assertion, so you immediately know if either `to`, `cc` or `bcc` didn't contain the expected addresses.

You could opt to pass a custom message as a second argument to `assertTrue`. That message will be displayed when the assertion fails.

```php
Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
   $this->assertTrue($mail->hasTo($user->email), 'Unexpected to');
   $this->assertTrue($mail->hasCc('...'), 'Unexpected cc');
   $this->assertTrue($mail->hasBcc('...'), 'Unexpected bcc');

   return true;
});
```

## In conclusion #

Laravel's [fakes](https://laravel.com/docs/master/mocking) are fantastic. In this post, we've only used the mail specific one. But there are also fakes available for events, notifications, queues, ... Most of the assert methods on these fakes accept a callable as a second argument. Here's [a real life refactor](https://github.com/spatie/laravel-stripe-webhooks/commit/60bd1b154ef3ebb7668d408debb9e40bb41f8d64) from returning a boolean to using assertions.

You can make a failing test much more readable buy not just returning a boolean in the callables you pass, but to use PHPUnit assertion methods inside of them.

Have fun testing!