Testing Mail

Previewing, Queueing, and Testing Mail

A route returning new OrderShipped($order) previews the mail in a browser, and ->render() returns the same 13,112 bytes of HTML (the figure in Mailables). MAIL_MAILER=log writes the MIME message, structured as in Attachments and MIME Structure, to laravel.log:

Output of 167
[2026-09-23 08:54:56] local.DEBUG: From: Bookshop Orders <orders@example.com>
To: Ana Souza <ana@example.com>
Subject: Order #1 has shipped
...
Content-Type: multipart/mixed; boundary=7srLNwog

Production code queues mail with ->queue() or ShouldQueue (Queues and Scheduling). The job records the mailer that was default when queued: mail queued under log went to the log although the worker ran with smtp.

Tests use fakes. From tests/Feature/OrderMailTest.php, where order() builds an unsaved order and Storage::fake('local') holds a stand-in invoice:

Asserting on a mailable's content and on queued mailPHP
$mail = new OrderShipped($this->order());
$mail->assertSeeInHtml('Hi Ana Souza');
$mail->assertHasAttachedData('%PDF-1.4', 'invoice-1.pdf', ['mime' => 'application/pdf']);
Mail::fake();                                           // in the second test
Mail::to($order->customer)->queue(new OrderShipped($order));
Mail::assertQueued(OrderShipped::class, fn ($m) => $m->hasTo('ana@example.com'));
Mail::assertNothingSent();
Output
  ✓ the mailable renders and attaches the invoice                        0.16s
  ✓ mail is queued and the notification uses two channels                0.01s
  Tests:    2 passed (7 assertions)

fromStorageDisk() passes the bytes to attachData(), so assertHasAttachmentFromStorageDisk() failed here and assertHasAttachedData() matched (Mocks and Fakes).