PHPackages                             zenstruck/mailer-test - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. zenstruck/mailer-test

ActiveLibrary[Testing &amp; Quality](/categories/testing)

zenstruck/mailer-test
=====================

Alternative, opinionated helpers for testing emails sent with symfony/mailer.

v1.5.0(4mo ago)46671.1k↓18%5[1 PRs](https://github.com/zenstruck/mailer-test/pulls)3MITPHPPHP &gt;=8.0CI passing

Since Feb 22Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/zenstruck/mailer-test)[ Packagist](https://packagist.org/packages/zenstruck/mailer-test)[ Docs](https://github.com/zenstruck/mailer-test)[ GitHub Sponsors](https://github.com/kbond)[ GitHub Sponsors](https://github.com/nikophil)[ RSS](/packages/zenstruck-mailer-test/feed)WikiDiscussions 1.x Synced 1mo ago

READMEChangelog (10)Dependencies (12)Versions (14)Used By (3)

zenstruck/mailer-test
=====================

[](#zenstruckmailer-test)

[![CI Status](https://github.com/zenstruck/mailer-test/workflows/CI/badge.svg)](https://github.com/zenstruck/mailer-test/actions?query=workflow%3ACI)[![Code Coverage](https://camo.githubusercontent.com/48ebac5980261a1a5742fcc98df020ef545b1af3b3e6f092ec8ae6bb0de159bf/68747470733a2f2f636f6465636f762e696f2f67682f7a656e73747275636b2f6d61696c65722d746573742f6272616e63682f312e782f67726170682f62616467652e7376673f746f6b656e3d52374f48595947504b4d)](https://codecov.io/gh/zenstruck/mailer-test)

Alternative, opinionated helpers for testing emails sent with `symfony/mailer`. This package is an alternative to the FrameworkBundle's `MailerAssertionsTrait`.

Tip

Want to watch a screencast 🎥 about it? Check out [symfonycasts.com/mailer-test](https://symfonycasts.com/mailer-test).

Installation
------------

[](#installation)

1. Install the library: ```
    composer require --dev zenstruck/mailer-test
    ```
2. If not added automatically by symfony/flex, enable `ZenstruckMailerTestBundle` in your `test` environment

Usage
-----

[](#usage)

You can interact with the mailer in your tests by using the `InteractsWithMailer` trait in your `KernelTestCase`/`WebTestCase` tests:

```
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Mailer\Test\InteractsWithMailer;
use Zenstruck\Mailer\Test\TestEmail;

class MyTest extends KernelTestCase // or WebTestCase
{
    use InteractsWithMailer;

    public function test_something(): void
    {
        // ...some code that sends emails...

        $this->mailer()->assertNoEmailSent();
        $this->mailer()->assertSentEmailCount(5);
        $this->mailer()->assertEmailSentTo('kevin@example.com', 'the subject');

        // For more advanced assertions, use a callback for the subject.
        // Note the \Zenstruck\Mailer\Test\TestEmail argument. This is a decorator
        // around \Symfony\Component\Mime\Email with some extra assertions.
        $this->mailer()->assertEmailSentTo('kevin@example.com', function(TestEmail $email) {
            $email
                ->assertSubject('Email Subject')
                ->assertSubjectContains('Subject')
                ->assertFrom('from@example.com')
                ->assertReplyTo('reply@example.com')
                ->assertCc('cc1@example.com')
                ->assertCc('cc2@example.com')
                ->assertBcc('bcc@example.com')
                ->assertTextContains('some text')
                ->assertHtmlContains('some text')
                ->assertContains('some text') // asserts text and html both contain a value
                ->assertHasFile('file.txt', 'text/plain', 'Hello there!')

                // tag/meta data assertions (https://symfony.com/doc/current/mailer.html#adding-tags-and-metadata-to-emails)
                ->assertHasTag('password-reset')
                ->assertHasMetadata('Color')
                ->assertHasMetadata('Color', 'blue')
            ;

            // Any \Symfony\Component\Mime\Email methods can be used
            $this->assertSame('value', $email->getHeaders()->get('X-SOME-HEADER')->getBodyAsString());
        });

        // reset collected emails
        $this->mailer()->reset();
    }
}
```

**NOTE**: Emails are persisted between kernel reboots within each test. You can reset the collected emails with `$this->mailer()->reset()`.

### SentEmails Collection

[](#sentemails-collection)

You can access all the sent emails and filter down to just the ones you want to make assertions on. Most methods are fluent.

```
use Symfony\Component\Mime\Email;
use Zenstruck\Mailer\Test\SentEmails;
use Zenstruck\Mailer\Test\TestEmail;

/** @var SentEmails $sentEmails */
$sentEmails = $this->mailer()->sentEmails();

$sentEmails->all(); // TestEmail[]
$sentEmails->raw(); // Email[]

$sentEmails->first(); // First TestEmail in collection or fail if none
$sentEmails->last(); // Last TestEmail in collection or fail
$sentEmails->count(); // # of emails in collection
$sentEmails->dump(); // dump() the collection
$sentEmails->dd(); // dd() the collection

$sentEmails->each(function(TestEmail $email) {
    // do something with each email in collection
});
$sentEmails->each(function(Email $email) {
    // can typehint as Email
});

// iterate over collection
foreach ($sentEmails as $email) {
    /** @var TestEmail $email */
}

// assertions
$sentEmails->assertNone();
$sentEmails->assertCount(5);

// fails if collection is empty
$sentEmails->ensureSome();
$sentEmails->ensureSome('custom failure message');

// filters - returns new instance of SentEmails
$sentEmails->whereSubject('some subject'); // emails with subject "some subject"
$sentEmails->whereSubjectContains('subject'); // emails where subject contains "subject"
$sentEmails->whereFrom('sally@example.com'); // emails sent from "sally@example.com"
$sentEmails->whereTo('sally@example.com'); // emails sent to "sally@example.com"
$sentEmails->whereCc('sally@example.com'); // emails cc'd to "sally@example.com"
$sentEmails->whereBcc('sally@example.com'); // emails bcc'd to "sally@example.com"
$sentEmails->whereReplyTo('sally@example.com'); // emails with "sally@example.com" as a reply-to
$sentEmails->whereTag('password-reset'); // emails with "password-reset" tag (https://symfony.com/doc/current/mailer.html#adding-tags-and-metadata-to-emails)

// custom filter
$sentEmails->where(function(TestEmail $email): bool {
    return 'password-reset' === $email->tag() && 'Some subject' === $email->getSubject();
});

// combine filters
$sentEmails
    ->whereTag('password-reset')
    ->assertCount(2)
    ->each(function(TestEmail $email) {
        $email->assertSubjectContains('Password Reset');
    })
    ->whereTo('kevin@example.com')
    ->assertCount(1)
```

### Custom TestEmail

[](#custom-testemail)

The `TestEmail` class shown above is a decorator for `\Symfony\Component\Mime\Email`with some assertions. You can extend this to add your own assertions:

```
namespace App\Tests;

use PHPUnit\Framework\Assert;
use Zenstruck\Mailer\Test\TestEmail;

class AppTestEmail extends TestEmail
{
    public function assertHasPostmarkTag(string $expected): self
    {
        Assert::assertTrue($this->getHeaders()->has('X-PM-Tag'));
        Assert::assertSame($expected, $this->getHeaders()->get('X-PM-Tag')->getBodyAsString());

        return $this;
    }
}
```

Then, use in your tests:

```
use App\Tests\AppTestEmail;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Mailer\Test\InteractsWithMailer;

class MyTest extends KernelTestCase // or WebTestCase
{
    use InteractsWithMailer;

    public function test_something(): void
    {
        // ...some code that sends emails...

        // Type-hinting the callback with your custom TestEmail triggers it to be
        // injected instead of the standard TestEmail.
        $this->mailer()->assertEmailSentTo('kevin@example.com', function(AppTestEmail $email) {
            $email->assertHasPostmarkTag('password-reset');
        });

        $this->mailer()->sentEmails()->each(function(AppTestEmail $email) {
            $email->assertHasPostmarkTag('password-reset');
        });

        // add your custom TestEmail as an argument to these methods to change the return type
        $this->mailer()->sentEmails()->first(AppTestEmail::class); // AppTestEmail
        $this->mailer()->sentEmails()->last(AppTestEmail::class); // AppTestEmail
        $this->mailer()->sentEmails()->all(AppTestEmail::class); // AppTestEmail[]
    }
}
```

### zenstruck/browser Integration

[](#zenstruckbrowser-integration)

This library provides a [zenstruck/browser](https://github.com/zenstruck/browser)"[Component](https://github.com/zenstruck/browser#custom-components)" and "[Extension](https://github.com/zenstruck/browser#custom-browser)". Since browser's make HTTP requests to your app, the messages are accessed via the profiler (using `symfony/mailer`'s data collector). Because of this, the `InteractsWithMailer` trait is not required in your test case. Since the profiler is required, this functionality is not available with `PantherBrowser`.

#### MailerComponent

[](#mailercomponent)

The simplest way to get started testing emails with `zenstruck/browser` is to use the `MailerComponent`:

```
use Zenstruck\Mailer\Test\Bridge\Zenstruck\Browser\MailerComponent;
use Zenstruck\Mailer\Test\TestEmail;

/** @var \Zenstruck\Browser\KernelBrowser $browser **/
$browser
    ->withProfiling() // enable the profiler for the next request
    ->visit('/page/that/does/not/send/email')
    ->use(function(MailerComponent $component) {
        $component->assertNoEmailSent();
    })

    ->withProfiling() // enable the profiler for the next request
    ->visit('/page/that/sends/email')
    ->use(function(MailerComponent $component) {
        $component
            ->assertSentEmailCount(1)
            ->assertEmailSentTo('kevin@example.com', 'Email Subject')
            ->assertEmailSentTo('kevin@example.com', function(TestEmail $email) {
                // see Usage section above for full API
            })
        ;

        $component->sentEmails(); \Zenstruck\Mailer\Test\SentEmails
    })
;
```

#### MailerExtension

[](#mailerextension)

If many of your tests make email assertions the [MailerComponent](#mailercomponent)'s API can be a little verbose. Alternatively, you can add the methods directly on a [custom browser](https://github.com/zenstruck/browser#custom-browser) using the provided `MailerExtension` trait:

```
namespace App\Tests;

use Zenstruck\Browser\KernelBrowser;
use Zenstruck\Mailer\Test\Bridge\Zenstruck\Browser\MailerExtension;

class AppBrowser extends KernelBrowser
{
    use MailerExtension;
}
```

Now, within your tests using this custom browser, the following email assertion API is available:

```
use Zenstruck\Mailer\Test\TestEmail;

/** @var \App\Tests\AppBrowser $browser **/
$browser
    ->withProfiling() // enable the profiler for the next request
    ->visit('/page/that/does/not/send/email')
    ->assertNoEmailSent()

    ->withProfiling() // enable the profiler for the next request
    ->visit('/page/that/sends/email')
    ->assertSentEmailCount(1)
    ->assertEmailSentTo('kevin@example.com', 'Email Subject')
    ->assertEmailSentTo('kevin@example.com', function(TestEmail $email) {
        // see Usage section above for full API
    })
;
```

###  Health Score

57

—

FairBetter than 98% of packages

Maintenance80

Actively maintained with recent releases

Popularity50

Moderate usage in the ecosystem

Community19

Small or concentrated contributor base

Maturity65

Established project with proven stability

 Bus Factor1

Top contributor holds 93.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~140 days

Recently: every ~211 days

Total

14

Last Release

92d ago

Major Versions

v0.4.0 → v1.0.02021-11-19

v0.5.0 → v1.1.02022-04-07

PHP version history (2 changes)v0.1.0PHP &gt;=7.4

v1.3.0PHP &gt;=8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/707369cc916e0ea1aacbf077dcba464f611cef879f024d8944311a54a15224b3?d=identicon)[kbond](/maintainers/kbond)

---

Top Contributors

[![kbond](https://avatars.githubusercontent.com/u/127811?v=4)](https://github.com/kbond "kbond (70 commits)")[![Chris53897](https://avatars.githubusercontent.com/u/7104259?v=4)](https://github.com/Chris53897 "Chris53897 (3 commits)")[![norkunas](https://avatars.githubusercontent.com/u/2722872?v=4)](https://github.com/norkunas "norkunas (1 commits)")[![OskarStark](https://avatars.githubusercontent.com/u/995707?v=4)](https://github.com/OskarStark "OskarStark (1 commits)")

---

Tags

symfonytestdevmailer

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/zenstruck-mailer-test/health.svg)

```
[![Health](https://phpackages.com/badges/zenstruck-mailer-test/health.svg)](https://phpackages.com/packages/zenstruck-mailer-test)
```

###  Alternatives

[zenstruck/foundry

A model factory library for creating expressive, auto-completable, on-demand dev/test fixtures with Symfony and Doctrine.

78611.9M97](/packages/zenstruck-foundry)[zenstruck/browser

A fluent interface for your Symfony functional tests.

2272.2M21](/packages/zenstruck-browser)[zenstruck/messenger-test

Assertions and helpers for testing your symfony/messenger queues.

2774.8M13](/packages/zenstruck-messenger-test)[zenstruck/console-test

Alternative, opinionated helper for testing Symfony console commands.

58635.0k20](/packages/zenstruck-console-test)[liip/test-fixtures-bundle

This bundles enables efficient loading of Doctrine fixtures in functional test-cases for Symfony applications

1798.3M42](/packages/liip-test-fixtures-bundle)[certificationy/certificationy

A library to create certification system

46930.7k1](/packages/certificationy-certificationy)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
