FrankMawn wrote a comment+100 XP
3mos ago
FrankMawn liked a comment+100 XP
6mos ago
You’re right—Laravel's built-in Mail::fake() and Notification::fake() only let you make assertions about the recipients, subjects, mailable classes, etc., but not about the low-level headers on the underlying Swift or Symfony message.
To test that custom headers are actually set on your outgoing mail, you need to avoid faking notifications or mail, and instead use a custom mail transport or mailbox that captures the built Symfony\Component\Mime\Email or, in older Laravel versions, the Swift_Message.
Here’s a practical approach for Laravel 9+, which uses Symfony Mailer:
1. Use assertSent with a callback and tap into the message
If your Notifiable uses the toMail method and in it adds headers via the withSymfonyMessage, you can use Mail::assertSent() with a callback to inspect the message:
Mail::assertSent(YourMailable::class, function ($mail) {
$headers = null;
$mail->withSymfonyMessage(function (\Symfony\Component\Mime\Email $message) use (&$headers) {
// Access headers
$headers = $message->getHeaders();
});
// Now you can check your header
return $headers && $headers->has('Your-Header-Name') &&
$headers->get('Your-Header-Name')->getBody() === 'your-value';
});
But: This works only if your mailable exposes the headers via the withSymfonyMessage tap. The assertion is against the mailable object, not the real sent message.
2. (Recommended) Use a Custom Transport That Captures Messages
Instead of faking mail, configure a custom Symfony Transport that keeps the messages in memory for assertions:
namespace Tests;
use Illuminate\Support\Facades\Mail;
use Symfony\Component\Mailer\Transport\NullTransport;
use Symfony\Component\Mime\Email;
class CaptureTransport extends NullTransport
{
public $messages = [];
public function send(\Symfony\Component\Mime\RawMessage $message, ?\Symfony\Component\Mailer\Envelope $envelope = null): void
{
parent::send($message, $envelope);
$this->messages[] = $message;
}
}
Usage in your test:
public function test_email_has_custom_header()
{
$transport = new \Tests\CaptureTransport();
Mail::mailer()->setSymfonyTransport($transport);
// Trigger notification/mailable
Notification::route('mail', '[email protected]')->notify(new YourNotification);
/** @var Symfony\Component\Mime\Email $message */
$message = $transport->messages[0];
$this->assertTrue(
$message->getHeaders()->has('Your-Header-Name'),
'Custom header not found'
);
$this->assertEquals(
'value-you-set',
$message->getHeaders()->get('Your-Header-Name')->getBody()
);
}
Notes
- If you are using older Laravel versions or SwiftMailer, the approach is similar but you'd need to access the
Swift_Messageobject. - This pattern lets you test the actual built message, not just the mailable logic.
References:
Summary:
- For simple cases, use
Mail::assertSent()and inspect the headers via the mailable. - For certainty, use a custom in-memory transport to capture the full message and assert on the actual headers as the message is delivered.
Let me know if you want a SwiftMailer (Laravel 8.x and below) example!
FrankMawn wrote a reply+100 XP
6mos ago
Went with Lary's recommended approach of using a custom Transport driver and was able to successfully do assertions on the mail message headers.
Note that his provided code had a few issues like extending a final class and some method signature errors but nothing that couldn't be fixed.
Thanks LARY.
FrankMawn started a new conversation+100 XP
6mos ago
Laravel docs provides a way to add custom email header.
Does anyone know how to assert that the header was included when sending the mail message? I've tried everything (Notification fake, Mail fake) and can't seem to be able to hook into the mail building process to do the assertion on the header.