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(5mo ago)45747.8k↓24.9%5[4 issues](https://github.com/zenstruck/mailer-test/issues)[1 PRs](https://github.com/zenstruck/mailer-test/pulls)3MITPHPPHP &gt;=8.0CI passing

Since Feb 22Pushed 4mo 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 2d 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

55

—

FairBetter than 97% of packages

Maintenance71

Regular maintenance activity

Popularity50

Moderate usage in the ecosystem

Community21

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

138d 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/browser

A fluent interface for your Symfony functional tests.

2262.6M43](/packages/zenstruck-browser)[zenstruck/foundry

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

79613.7M150](/packages/zenstruck-foundry)[zenstruck/messenger-test

Assertions and helpers for testing your symfony/messenger queues.

2755.7M23](/packages/zenstruck-messenger-test)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.4M203](/packages/sulu-sulu)[kimai/kimai

Kimai - Time Tracking

4.8k9.0k1](/packages/kimai-kimai)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.6M574](/packages/shopware-core)

PHPackages © 2026

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