Laravel unit testing emails
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Mastering Email Testing in Laravel: Beyond Simple Logging
When building complex applications, side effects like sending emails are critical components. You need assurance that when a user signs up or places an order, the correct notification is generated and dispatched. The question, "What is the best way to unit test that?" is one every developer faces.
Many developers default to checking logs—which is helpful for debugging production issues—but logging alone is not sufficient for robust unit testing. We need actual verification that the mailer logic executes correctly without relying on external services or complex setup.
As a senior developer, I recommend moving beyond simple logging and focusing on mocking dependencies and asserting behavior within your tests. Let's dive into the practical strategies for unit testing emails in Laravel.
Why Logging Isn't Enough for Unit Testing
You mentioned seeing functionality in "pretend mode" or logs. While logging is invaluable during development to track execution flow, it falls short as a formal unit test for several reasons:
- Brittleness: Log entries can be easily missed, misinterpreted, or overwritten, making tests brittle.
- Isolation: Unit tests should test the logic of your code in isolation, not the interaction with the filesystem or logging system, unless that interaction is the direct subject of the test.
- Failure Mode: If a mailer fails to render correctly but still logs an entry, your test might pass, leading to silent failures in production.
The goal of unit testing here is to verify that the Mailable class is constructed correctly and that the necessary data is available for sending, rather than actually connecting to an SMTP server.
Strategy 1: Unit Testing the Mailable Content
The first layer of testing involves ensuring your Mailable classes are generating the correct content based on the data provided. This is pure unit testing. We test the mailer class in isolation to ensure it formats the email body, subject, and attachments exactly as expected.
Consider a simple OrderNotification Mailable:
// app/Mail/OrderNotification.php
class OrderNotification extends Mailable
{
use Queueable;
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
public function build()
{
return $this->view('emails.order_details');
}
}
To test this, you mock the necessary data and assert that the resulting output (or the structure of the built mail) is correct. While directly testing the rendered HTML can be complex, we ensure the constructor correctly receives the required Eloquent model. This aligns with good object-oriented design principles, much like how you manage dependencies in Laravel services found on laravelcompany.com.
Strategy 2: Testing Queue Dispatching via Mocking
For testing the broader flow—ensuring that when an event occurs (e.g., creating an order), the email job is correctly placed onto the queue—we use Laravel's testing facilities to mock the Mail facade or the Job itself.
Instead of letting your test attempt to send a real email, we instruct Laravel’s testing environment to intercept the sending process. This allows us to verify that the correct Mailable object was passed to the queue mechanism without incurring external service costs or dependencies.
A typical approach involves binding a mock mailer or using the Bus facade to assert that a job containing your Mailable was dispatched successfully. This focuses the test purely on the application logic rather than external infrastructure.
use Illuminate\Support\Facades\Mail;
use App\Models\Order;
public function test_order_notification_is_queued()
{
$order = Order::factory()->create();
// Mocking the mailer interaction to ensure dispatch happens correctly
Mail::fake();
// Attempt to send the email (this queues it internally)
Mail::to('test@example.com')->send(new OrderNotification($order));
// Assertion: Check if the fake mailer received exactly one attempt
Mail::assertSent(OrderNotification::class, 1);
}
Conclusion
Testing emails effectively requires shifting your focus from infrastructure concerns (SMTP setup) to application logic concerns (data preparation and queuing). By employing mocking techniques—testing the Mailable content in isolation and asserting queue dispatching behavior—you create unit tests that are fast, reliable, and truly reflect whether your Laravel application is behaving as intended. This disciplined approach is fundamental to building scalable applications on the Laravel framework.