Prevent Accidental Email Sends

August 11th, 2022

Chipper CI is continuous integration, just for Laravel. Give it a try - free!

Sending emails in non-production environments is a common mistake. This is embarassing!

I've made this mistake before, and so I've developed a list of ways to ensure it doesn't happen again!

Here's my bag-of-tricks for making sure I don't send accidental emails, brought to you by painful experience.

Mail Fakes

Using mail fakes is the primary means of ensuring you don't send real emails when running a Laravel app's test suite.

In this case, you can add Mail::fake() to each test that might send an email. This is ideal because no mail will be sent out, and you don't need to care about switching mail drivers, etc.

Here's an example Feature test tests/Feature/SomeControllerTest.php

<?php

namespace Tests/Feature;

use App\Mail\SomeEmailWeSend;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;

class SomeControllerTest extends TestCase
{
        public function test_some_action_was_done()
    {
        // Ensure we don't send emails
        Mail::fake();

        // Do some work

        // Some example assertions you might
        // want to make in your tests
        Mail::assertNothingSent();                   // assertNothingQueued
        Mail::assertSent(SomeEmailWeSend::class);    // assertQueued
        Mail::assertNotSent(SomeEmailWeSend::class); // assertNotQueued
    }
}

This is great - we can both stop emails from sending, and test that the application sent (or queued up) some emails to send (or not)!

The downside of this is that if you miss running Mail::fake(), you could accidentally send an email! So, I use Mail fakes in conjunction with some of the other options here.

Setting Global Addresses

Laravel allows you to set the application to always send to a specific address. You may want to set this for any non-production environment.

This is done via Mail::alwaysTo(). I always add the following to my app/Providers/AppServiceProvider.php class:

use Illuminate\Support\Facades\Mail;

// later, within the AppServiceProvider class...

public function boot()
{
    // If we're not production or staging, use a fake address
    if (! $this->app->environment('production', 'staging')) {
            Mail::alwaysTo('testing@example.org');
    }
}

It's generally safe to send test emails to the @example.org domain. It actually exists for this purpose! That being said, I still wouldn't go sending personal information there.

Some neat facts about this method:

  1. This prevents CC and BCC addresses from being sent as well.
  2. There's also a Mail::alwaysFrom() method

Staging Environments

Whether or not you set your staging environment to send "real" emails is up to you.

If you do something crazy like copying production data into staging, you may not wish to allow staging to send real emails. Who would do such a thing? Well, this article exists because yours-truly did just that!

Setting a No-Op Driver

There's a mail driver you can use that stops Laravel from sending emails! Instead, it will log emails out to your set log channel (the laravel.log file by default).

This can be done by setting environment variable MAIL_MAILER to log.

MAIL_MAILER=log

You can also force this in your AppServiceProvider if you'd like:

use Illuminate\Support\Facades\Mail;

// later, within the AppServiceProvider class...

public function boot()
{
    // If we're not production or staging, use a fake address
    if (! $this->app->environment('production', 'staging')) {
            Mail::alwaysTo('testing@example.org');

            // Also force the "log" mailer
            config()->set('mail.default', 'log')
    }
}

Using a Service

Finally, if you need more advanced features (inspecting the emails being sent, storing them, etc), there are some handy services you can use!

Where and how you use these ranges from local apps, to cloud-based services, to apps you can run on servers and within CI pipelines.

  1. HELO works great for local development - it's a local application you can download
  2. Mailtrap is an “actual” smtp service you can use. You just set your mail driver to smtp and configure Mailtrap's remote host
  3. Mailhog can be installed locally (or on servers / within CI pipelines) and be used with the smtp mail driver
Try out Chipper CI!
Chipper CI is the easiest way to test and deploy your Laravel applications. Try it out - it's free!