Laravel Email Testing: Mailtrap vs. Other Options

On September 01, 2020
7min read
Piotr Malek Technical Content Writer @ Mailtrap

Laravel is arguably the most popular PHP frameworks and that’s unlikely to change any time soon. Developers love it because of its ability to handle complex tasks with ease—plus, it operates at high speed and has excellent security features. As you might expect, it’s also really easy to set up for email testing.

Today, we’ll discuss three great methods for email testing in Laravel. We explain what you can expect from each and how to set them up easily.

Building an example email app in Laravel

We assume that you already have a Laravel app that you want to use to try email testing capability. Launch it now, open the terminal and enter the following:

php artisan make:mail JustTesting

This will create a new mailable class in app/Mail directory. If you haven’t created one before, it will also set up the directory.

Now we can go ahead and build our test message:

public function build()
{
    return $this->from('hello@mailtrap.io')
		        ->to('bonjour@mailtrap.io')
		        ->cc('hola@mailtrap.io')
                   ->subject('Auf Wiedersehen')
                   ->markdown('mails.exmpl')
                   ->with([
                     'name' => 'New Mailtrap User',
                     'link' => '/inboxes/'
                   ]);
    }
}

To spice it up a bit, let’s use a good-looking template. For that, we can use Laravel’s templating engine, Blade

Templates represent views in Laravel, so keep the template file in the resources/views/mails directory. Then, we’ll create a ‘mails’ directory with a Blade template file ‘exmpl.blade.php’ inside.

@component('mail::message')
Hello **{{$name}}**,  {{-- use double space for line break --}}
Thank you for choosing Mailtrap!
Click below to start working right now
@component('mail::button', ['url' => $link])
Go to your inbox
@endcomponent
Sincerely,
Mailtrap team.
@endcomponent

If you’re not sure how exactly this works or want to customize a message even further, please refer to the excellent Laravel Docs for more details. Be sure to also check out our articles on sending emails in Laravel and Laravel Email Queues for more on the topic.

Testing emails in Laravel

Now, let’s try testing our message with some different available methods. If you haven’t yet, make sure you’ve configured a driver for email sending

Using Tinker to validate individual emails

A super-simple approach to email testing is with Laravel’s built-in utility known as Tinker. Tinker is a REPL (read-eval-print loop) tool. It takes a single input (for example, a request to send an email), evaluates it, and immediately returns a detailed response afterward. You can leverage the tinker functionality for Laravel and any other PHP framework with Tinkerwell, the code runner that allows you to quickly test out any PHP code in the context of your application.

It’s really handy if you set up an integration with an ESP and need to test its efficacy. In our case, the delivery will be successful, or we’ll commit one of many possible errors. These could be related to the email configuration (missing ‘From’ address, etc.) or sending errors. You may, for example, get a response about an unsuccessful authentication, failed connection, or an account that needs activation, to name a few.

To run Tinker, launch the terminal and type in the following:

php artisan tinker

Then, run the following sample code:

use App\Mail\JustTesting;
Mail::send(new JustTesting());

If everything works out, you’ll see a not-very-descriptive ‘null’ response:

>>> Mail::send(new JustTesting());
=> null

If you forgot to introduce yourself (skipped the ‘From’ field), you’ll get the following error:

>>> Mail::send(new JustTesting());
Swift_TransportException with message 'Cannot send message without a sender address'

If the SMTP authentication fails, you’ll see the following:

>>> Mail::send(new JustTesting());
Swift_TransportException with message 'Failed to authenticate on SMTP server with username "807a39dc3703e0" using 3 possible authenticators. Authenticator CRAM-MD5 returned Expected response code 235 but got code "535", with message "535 5.7.0 Invalid login or password
". Authenticator LOGIN returned Expected response code 250 but got an empty response. Authenticator PLAIN returned Expected response code 250 but got an empty response.'

And if you got creative and wanted to send your test emails to hey@hi@halo@mailtrap@io, you’re likely to encounter the following error:

>>> Mail::send(new JustTesting());
Swift_RfcComplianceException with message 'Address in mailbox given [hey@hi@halo@mailtrap@io] does not comply with RFC 2822, 3.6.2.'

Mocking emails in automated tests

Since the 5.3 release of Laravel (Aug ‘16), built-in functionalities allow for mocking certain elements of applications in automated tests. This way, you can test their performance without actually executing them. As you can imagine, it’s particularly useful for emails.

Laravel provides helpers for mocking events, facades, and other jobs so that you don’t have to process complex Mockery method calls. However, if you want to play with Mockery or PHPUnit, for example, you can do that, as well.

For emails, there’s a dedicated fake method in the Mail class that prevents emails from being sent but lets you test the sending capability. After the respective code is executed, you can call various assertions to check if an email was sent successfully.

If an assertion fails, you’ll receive an error with a possible cause, similar to what Tinker would return.

To create a test for your mailer, run the following command in the project directory:

php artisan make:test MailerTest --unit

This will generate a file called ‘MailerTest.php’ in the ‘tests/Unit’ folder of your project.

Now, modify the generated file to add some tests for your mailer:

<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Mail\JustTesting;
use Illuminate\Support\Facades\Mail;
class MailerTest extends TestCase
{
   /**
    * A basic unit test example.
    *
    * @return void
    */
   public function testSending()
   {
       Mail::fake();
       Mail::send(new JustTesting());
       Mail::assertSent(JustTesting::class);
       Mail::assertSent(JustTesting::class, function ($mail) {
           $mail->build();
           $this->assertTrue($mail->hasFrom('hello@mailtrap.io'));
           $this->assertTrue($mail->hasCc('hola@mailtrap.io'));
           return true;
       });
   }
}

Now, run your test with the following command:

php artisan test tests/Unit/MailerTest.php

If your code works correctly, you’ll see the following result:

PASS  Tests\Unit\MailerTest
  ✓ sending
  Tests:  1 passed
  Time:   0.02s

If something has gone wrong, like if your code didn’t set the ‘from’ field correctly, the test run will fail:

  FAIL  Tests\Unit\MailerTest
  ✕ sending
  Tests:  1 failed
  Failed asserting that false is true.
  at tests/Unit/MailerTest.php:26
	    	Mail::assertSent(JustTesting::class);
	    	Mail::assertSent(JustTesting::class, function ($mail) {
		       	$mail->build();
  	      	$this->assertTrue($mail->hasFrom('hello@mailtrap.io'));
		        	$this->assertTrue($mail->hasCc('hola@mailtrap.io'));
		        	return true;
	    	});
  	+1 vendor frames
  2   [internal]:0
  	Illuminate\Support\Testing\Fakes\MailFake::Illuminate\Support\Testing\Fakes\{closure}(Object(App\Mail\JustTesting))
  	+5 vendor frames
  8   tests/Unit/MailerTest.php:30
  	Illuminate\Support\Facades\Facade::__callStatic("assertSent")

When in doubt, refer to the official Mocking documentation.

Keep in mind that this will only work with Laravel 5.3 and newer. If you still use 5.1 or 5.2 where Mail::fake() is not available, you can try a workaround. With these versions, the shouldReceive method returns an instance of Mockery mock, so you can still mock the calls to the facade with the following:

<?php
Mail::shouldReceive('send')->once();

The test is not very strict, as we’re focused on testing the core sending functionality, which you can also freely adjust.

Mailtrap Email Testing

A much more robust solution than the two described above is Mailtrap Email Testing, which is an inseparable part of Mailtrap Email Delivery Platform that lets you test, send, and control your email infrastructure in one place.

All of the testing is done in a safe environment, where you can preview and debug emails without worrying they’ll accidentally be sent out.

One of the stand-out features of Mailtrap Email Testing is its HTML Check feature. Using it, you can see how supported by top email clients the HTML/CSS in your emails is. This information is crucial for knowing what percentage of your recipients will see your email in its true form (the way you want it to be seen) and how many will see HTML/CSS code poorly rendered by their email client.

You can also preview your emails in HTML, text, raw, etc., and see how they look before you send them out, ensuring they reach your recipients’ inboxes in a mint condition.

And considering that unsupported HTML/CSS is not the only issue your emails could have, Email Testing also provides you with a tool for testing your email content spam score, as well as checking the presence of your domain on blacklists.

Other valuable information you can find for each email tested with Email Testing includes SMTP transaction and email headers info.

Moreover, you can also test your email templates with our API and then easily switch from staging to production when you think you’re ready. Simply enable sandbox, specify the inbox ID to receive template test, and send it through the API in production.

Okay, Email Testing has quite cool features. But what other benefits does it have?

  • Saving time – it has a 5-minute setup, unlike SMTP servers, whose setup takes hours.
  • Using automation – With Email Testing, you can switch from manual testing to automated test flows and scenarios.
  • Preserving your domain reputation  – Using the offered test inbox removes the need to use your personal inbox for email testing, thus keeping your domain reputation safe.
  • Auto-forwarding and manual forwarding – easy email forwarding to selected recipients (automatically or manually) for testing purposes.

How to set up Email Testing in Laravel

As mentioned above, setting up Email Testing is quite an easy task, and here is how it’s done in Laravel.

First, get yourself a free Mailtrap account. This will give you access to the entire Mailtrap Email Delivery Platform consisting of Email Testing as well as Email Sending (a reliable and hassle-free email API/SMTP service). 

Then, once you are logged into the Mailtrap dashboard, navigate to Email Testing-> Inboxes and find the SMTP Settings page for the selected inbox.

On the page, you should see an Integrations section with a dropdown menu on which you should select Laravel 7+.

This will generate a code snippet that should go in the .env file located in the root directory of your project.

MAIL_MAILER=smtp
MAIL_HOST=sandbox.smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=07f4dbf61b8122
MAIL_PASSWORD=d7fc5d57bc6eac
MAIL_ENCRYPTION=tls

Please do keep in mind that you need to generate your own code snippet. This one is just for demonstration purposes.

With the integration complete, you can now move on to sending the first test email, and for that, you can use the following code:

<?php
use App\Mail\JustTesting;
use Illuminate\Support\Facades\Mail;
Route::get('/send-mail', function () {
    Mail::to('newuser@example.com')->send(new JustTesting());
    return 'A message has been sent to Mailtrap!';
});

Note: For this code to work, make sure you are using Laravel 5.8 or a newer version and that you have specified the route in the routes/web.php file. 

To send your test email, start the application and access/send-mail path in your browser. Shortly after, you should see the email in Email Testing-> Inboxes.

Alternatively, you can integrate Email Testing into your Laravel app via an API. For more details, refer to the API documentation.

Wrapping up

Each of the tools we’ve described can be helpful in some way. Tinker is really handy if you’ve already set up an integration with an ESP and wish to quickly try it out. The error messages are quite descriptive and can help you pinpoint the errors you may have missed before. Mocking also offers similar functionality but is more suitable for automated tests.

Still, there’s a lot more to email testing than all that. If you want to see precisely what your platform sends, quickly debug errors, and report with ease on your progress, you need a dedicated environment.

There are plenty of reasons why thousands of Laravel developers have trusted Mailtrap Email Testing. If you haven’t yet, get an account right away and see how easy email testing can be.

Article by Piotr Malek Technical Content Writer @ Mailtrap