Laravel unit testing emails

For Laravel 5.4 check Mail::fake(): https://laravel.com/docs/5.4/mocking#mail-fake


There are two options.

Option 1 - Mock the mail facade to test the mail is being sent. Something like this would work:

$mock = Mockery::mock('Swift_Mailer');
$this->app['mailer']->setSwiftMailer($mock);
$mock->shouldReceive('send')->once()
    ->andReturnUsing(function($msg) {
        $this->assertEquals('My subject', $msg->getSubject());
        $this->assertEquals('[email protected]', $msg->getTo());
        $this->assertContains('Some string', $msg->getBody());
    });

Option 2 is much easier - it is to test the actual SMTP using MailCatcher.me. Basically you can send SMTP emails, and 'test' the email that is actually sent. Laracasts has a great lesson on how to use it as part of your Laravel testing here.


"Option 1" from "@The Shift Exchange" is not working in Laravel 5.1, so here is modified version using Proxied Partial Mock:

$mock = \Mockery::mock($this->app['mailer']->getSwiftMailer());
$this->app['mailer']->setSwiftMailer($mock);
$mock
    ->shouldReceive('send')
    ->withArgs([\Mockery::on(function($message)
    {
        $this->assertEquals('My subject', $message->getSubject());
        $this->assertSame(['[email protected]' => null], $message->getTo());
        $this->assertContains('Some string', $message->getBody());
        return true;
    }), \Mockery::any()])
    ->once();

If you just don't want the e-mails be really send, you can turn off them using the "Mail::pretend(true)"

class TestCase extends Illuminate\Foundation\Testing\TestCase {
    private function prepareForTests() {
      // e-mail will look like will be send but it is just pretending
      Mail::pretend(true);
      // if you want to test the routes
      Route::enableFilters();
    }
}

class MyTest extends TestCase {
    public function testEmail() {
      // be happy
    }
}