zenstruck/mailer-test
Opinionated helpers to test emails sent with symfony/mailer. Use InteractsWithMailer in KernelTestCase/WebTestCase for fluent assertions like assertNoEmailSent, assertSentEmailCount, and inspect messages with TestEmail (subject, recipients, body, attachments).
MailerAssertionsTrait. It aligns well with Laravel’s testing ecosystem (e.g., KernelTestCase, WebTestCase) and Symfony’s symfony/mailer integration.symfony/mailer + symfony/mime). The InteractsWithMailer trait abstracts Symfony-specific dependencies, making it modular.TestEmail classes allow domain-specific assertions (e.g., Postmark tags), which is valuable for Laravel apps using third-party email services.symfony/mailer (Laravel 9+ supports this via spatie/laravel-mail or native symfony/mailer integration).InteractsWithMailer trait assumes Symfony’s KernelTestCase/WebTestCase; Laravel’s TestCase would need a thin wrapper or trait adaptation.Mail::fake()) is more mature, but this package offers advanced assertions (e.g., file attachments, metadata) not natively available.Mail::fake() if both are used. Recommendation: Use this package only for integration tests where Symfony’s mailer is explicitly required (e.g., hybrid Laravel/Symfony apps).symfony/mailer:^6.4).Mail::fake()). Risk: Memory leaks if tests don’t call reset().reset() in setUp() or tearDown().Mail::assertSent() or Mail::assertQueued(). Workaround: Use sentEmails()->dump() for debugging.Mail::fake()?
symfony/mailer is already used?reset() vs. custom test case setup.)zenstruck/browser integration) add overhead in CI?symfony/mailer and zenstruck/mailer-test as dev dependencies.spatie/laravel-mail or custom bridge).// config/mail.php
'driver' => 'symfony',
InteractsWithMailer to Laravel’s TestCase:
use Zenstruck\Mailer\Test\InteractsWithMailer as ZenstruckInteractsWithMailer;
use Illuminate\Foundation\Testing\TestCase;
trait InteractsWithMailer
{
use ZenstruckInteractsWithMailer;
protected function getMailer(): object
{
return app(\Symfony\Component\Mailer\MailerInterface::class);
}
}
zenstruck/mailer-test to validate assertions.Mail::fake() for parity.composer.json:
"require-dev": {
"symfony/mailer": "^6.4",
"zenstruck/mailer-test": "^1.5"
}
phpunit.xml:
<env name="KERNEL_CLASS" value="App\Providers\Kernel"/>
<env name="APP_ENV" value="test"/>
Mail::fake() for unit tests (simpler).zenstruck/mailer-test for integration tests (advanced assertions).| Feature | Laravel Native | zenstruck/mailer-test |
|---|---|---|
| Basic assertions | ✅ | ✅ |
| File attachment checks | ❌ | ✅ |
| Metadata/tags | ❌ | ✅ |
| Symfony profiler | ❌ | ✅ (for browser tests) |
Mail::fake() |
✅ | ❌ (conflict) |
symfony/mailer is integrated into Laravel.TestCase with InteractsWithMailer trait.zenstruck/mailer-test assertions.zenstruck/browser.Mail::fake().Mail::assertSent() chains.TestEmail classes can encapsulate app-specific logic (e.g., Postmark tags).symfony/mailer and its dependencies if not already present.Mail::fake() for unit tests.ZenstruckMailerTestBundle must be enabled in test environments.sentEmails()->dump() provides detailed email inspection.reset() in setUp() or use Laravel’s Mail::flush() as a fallback.--parallel).Mail::fake().| Scenario | Impact | Mitigation |
|---|---|---|
| Symfony version mismatch | Tests fail | Pin symfony/mailer to a stable version. |
| Email persistence leaks | Flaky tests | Reset emails in setUp() or use Mail::fake() for unit tests. |
| Profiler not enabled (browser) | Missing email data | Ensure withProfiling() is called. |
Custom TestEmail conflicts |
Assertion errors | Isolate custom classes per test suite. |
| Laravel/Symfony integration breaks | Mailer failures | Test with a hybrid Laravel/Symfony repo. |
How can I help you explore Laravel packages today?