Skip to content

Commit

Permalink
[feature][BC BREAK] emails persisted between reboots (closes #5) (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
kbond committed Oct 4, 2021
1 parent 5892976 commit 2399e87
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 45 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[*.yml]
indent_size = 2
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ an alternative to the FrameworkBundle's `MailerAssertionsTrait`.

## Installation

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

## Usage

Expand Down Expand Up @@ -62,12 +65,14 @@ class MyTest extends KernelTestCase // or WebTestCase
// Any \Symfony\Component\Mime\Email methods can be used
$this->assertSame('value', $email->getHeaders()->get('X-SOME-HEADER')->getBodyAsString());
});

$this->mailer()->sentTestEmails(); // TestEmail[]
}
}
```

**NOTE**: Emails are persisted between kernel reboots within each test.

### Custom TestEmail

The `TestEmail` class shown above is a decorator for `\Symfony\Component\Mime\Email`
Expand Down Expand Up @@ -143,7 +148,7 @@ $browser
->use(function(MailerComponent $component) {
$component->assertNoEmailSent();
})

->withProfiling() // enable the profiler for the next request
->visit('/page/that/sends/email')
->use(function(MailerComponent $component) {
Expand Down Expand Up @@ -188,7 +193,7 @@ $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)
Expand Down
28 changes: 21 additions & 7 deletions src/InteractsWithMailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@
*/
trait InteractsWithMailer
{
/**
* @internal
* @before
*/
final protected static function _startTestMailerCollection(): void
{
MessageEventCollector::start();
}

/**
* @internal
* @after
*/
final protected static function _resetTestMailerCollection(): void
{
MessageEventCollector::reset();
}

final protected function mailer(): TestMailer
{
if (!$this instanceof KernelTestCase) {
Expand All @@ -19,14 +37,10 @@ final protected function mailer(): TestMailer
throw new \LogicException('The kernel must be booted before accessing the mailer.');
}

if (self::$container->has('mailer.message_logger_listener')) {
return new TestMailer(self::$container->get('mailer.message_logger_listener')->getEvents());
}

if (self::$container->has('mailer.logger_message_listener')) {
return new TestMailer(self::$container->get('mailer.logger_message_listener')->getEvents());
if (!self::$container->has('zenstruck_mailer_test.event_collector')) {
throw new \LogicException(\sprintf('Cannot access collected emails - is %s enabled in your test environment?', ZenstruckMailerTestBundle::class));
}

throw new \LogicException('Mailer and/or profiling not enabled.');
return self::$container->get('zenstruck_mailer_test.event_collector')->mailer();
}
}
50 changes: 50 additions & 0 deletions src/MessageEventCollector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Zenstruck\Mailer\Test;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mailer\Event\MessageEvent;
use Symfony\Component\Mailer\Event\MessageEvents;

/**
* @author Kevin Bond <kevinbond@gmail.com>
*
* @internal
*/
final class MessageEventCollector implements EventSubscriberInterface
{
private static ?MessageEvents $events = null;

public function onMessage(MessageEvent $event): void
{
if (self::$events) {
self::$events->add($event);
}
}

public function mailer(): TestMailer
{
if (!self::$events) {
throw new \LogicException('Cannot access mailer as email collection has not yet been started.');
}

return new TestMailer(self::$events);
}

public static function start(): void
{
self::$events = new MessageEvents();
}

public static function reset(): void
{
self::$events = null;
}

public static function getSubscribedEvents(): array
{
return [
MessageEvent::class => ['onMessage', -255],
];
}
}
25 changes: 25 additions & 0 deletions src/ZenstruckMailerTestBundle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Zenstruck\Mailer\Test;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\HttpKernel\Bundle\Bundle;

/**
* @author Kevin Bond <kevinbond@gmail.com>
*/
final class ZenstruckMailerTestBundle extends Bundle
{
public function build(ContainerBuilder $container): void
{
$container->register('zenstruck_mailer_test.event_collector', MessageEventCollector::class)
->addTag('kernel.event_subscriber')
;
}

public function getContainerExtension(): ?ExtensionInterface
{
return null;
}
}
5 changes: 5 additions & 0 deletions tests/Fixture/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
use Zenstruck\Mailer\Test\ZenstruckMailerTestBundle;

/**
* @author Kevin Bond <kevinbond@gmail.com>
Expand All @@ -32,6 +33,10 @@ public function sendEmail(): Response
public function registerBundles(): iterable
{
yield new FrameworkBundle();

if ('no_bundle' !== $this->environment) {
yield new ZenstruckMailerTestBundle();
}
}

protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader): void
Expand Down
2 changes: 2 additions & 0 deletions tests/Fixture/config/no_bundle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
imports:
- { resource: test.yaml }
8 changes: 0 additions & 8 deletions tests/Fixture/config/no_mailer.yaml

This file was deleted.

46 changes: 22 additions & 24 deletions tests/InteractsWithMailerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Zenstruck\Mailer\Test\TestEmail;
use Zenstruck\Mailer\Test\Tests\Fixture\CustomTestEmail;
use Zenstruck\Mailer\Test\Tests\Fixture\Email1;
use Zenstruck\Mailer\Test\Tests\Fixture\Kernel;
use Zenstruck\Mailer\Test\ZenstruckMailerTestBundle;

/**
* @author Kevin Bond <kevinbond@gmail.com>
Expand Down Expand Up @@ -160,48 +160,46 @@ public function kernel_must_be_booted(): void
/**
* @test
*/
public function mailer_must_be_enabled(): void
public function profiler_does_not_need_to_enabled(): void
{
self::bootKernel(['environment' => 'no_mailer']);
self::bootKernel(['environment' => 'no_profiler']);

$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Mailer and/or profiling not enabled');
self::$container->get('mailer')->send(new Email1());

$this->mailer();
$this->mailer()->assertSentEmailCount(1);
}

/**
* @test
* @dataProvider environmentProvider
*/
public function profiler_must_be_enabled_in_symfony_5_2(): void
public function emails_are_persisted_between_reboots(string $environment): void
{
if ('52' !== Kernel::MAJOR_VERSION.Kernel::MINOR_VERSION) {
// profile needs to be enabled in 5.2 only
$this->markTestSkipped();
}
self::bootKernel(['environment' => $environment]);

self::bootKernel(['environment' => 'no_profiler']);
$this->mailer()->assertNoEmailSent();

$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Mailer and/or profiling not enabled');
self::$container->get('mailer')->send(new Email1());

$this->mailer();
$this->mailer()->assertSentEmailCount(1);

self::ensureKernelShutdown();

self::bootKernel(['environment' => $environment]);

$this->mailer()->assertSentEmailCount(1);
}

/**
* @test
*/
public function profiler_does_not_need_to_enabled(): void
public function bundle_must_be_enabled(): void
{
if ('52' === Kernel::MAJOR_VERSION.Kernel::MINOR_VERSION) {
// profile does not need to be enabled in versions other than 52
$this->markTestSkipped();
}
self::bootKernel(['environment' => 'no_bundle']);

self::bootKernel(['environment' => 'no_profiler']);

self::$container->get('mailer')->send(new Email1());
$this->expectException(\LogicException::class);
$this->expectExceptionMessage(\sprintf('Cannot access collected emails - is %s enabled in your test environment?', ZenstruckMailerTestBundle::class));

$this->mailer()->assertSentEmailCount(1);
$this->mailer();
}
}
27 changes: 27 additions & 0 deletions tests/NonInteractsWithMailerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Zenstruck\Mailer\Test\Tests;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Zenstruck\Mailer\Test\Tests\Fixture\Email1;

/**
* @author Kevin Bond <kevinbond@gmail.com>
*/
final class NonInteractsWithMailerTest extends KernelTestCase
{
/**
* @test
*/
public function ensure_emails_are_not_collected(): void
{
self::bootKernel();

self::$container->get('mailer')->send(new Email1());

$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Cannot access mailer as email collection has not yet been started.');

self::$container->get('zenstruck_mailer_test.event_collector')->mailer();
}
}

0 comments on commit 2399e87

Please sign in to comment.