geoB
geoB

Reputation: 4716

Return body of Symfony Mailer email in a functional test?

With Mailer, it appears not possible to retrieve the body of an email in a functional test. For example, with

$email = $this->getMailerMessage(0);
$body = $email->getBody();
var_dump($body);

$body is revealed as

object(Symfony\Component\Mime\Part\Multipart\AlternativePart)...

There are no (apparent) accessible properties of that object. Is there any method for accessing the text of the body of an email sent with Symfony Mailer in a functional test?

With SwiftMailer one could retrieve the body of an email in a functional test with:

$mailCollector = $this->client->getProfile()->getCollector('swiftmailer');
$collectedMessages = $mailCollector->getMessages();
$message = $collectedMessages[0];
$body = $message->getBody();

Upvotes: 0

Views: 1459

Answers (3)

Andrey Mashukov
Andrey Mashukov

Reputation: 11

  1. Create services_test.yaml near your services.yaml should look like this:
services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
        public: true

    test.mailer_symfony: '@Symfony\Component\Mailer\MailerInterface'
  1. In the setup method mock real service and change it in the service container
use Symfony\Component\Mailer\MailerInterface;

// setUp() 

$this->mailer = $this->createMock(MailerInterface::class);

self::$container->set('test.mailer_symfony', $this->mailer);
  1. Mock Method send() in you test, according this code https://github.com/symfony/mailer/blob/5.x/MailerInterface.php it should be something like:
use use Symfony\Component\Mime\RawMessage;

// ....

/** @var RawMessage[] $emailMessages */
$emailMessages = [];

$this->mailer
    ->method('send')
    ->willReturnCallback(function (RawMessage $message, Envelope $envelope = null) use (&$emailMessages) {
        $emailMessages[] = $message;
    });

that's all, you can assert $emailMessages in the end of your test, so, also you can add expects and check invocations amount of send() method

Upvotes: 0

Andrey Mashukov
Andrey Mashukov

Reputation: 11

So, let's start. At first create MOCK of you service, in your test like this:

$this->mailer = $this->createMock(\Swift_Mailer::class);

self::$container->set('swiftmailer.mailer.default', $this->mailer);

after define array variable for your messages in current test

/** @var null|\Swift_Message $emailMessage */
$emailMessages = [];

$this->mailer
    ->method('send')
    ->willReturnCallback(function (\Swift_Message $message) use (&$emailMessages) {
        $emailMessages[] = $message;
    });

so, we will catch each email to $emailMessages, you need just to choose needed message and assert its content like:

$emailMessage = $emailMessages[0] ?? null;

$this->assertNotNull($emailMessage);
$this->assertInstanceOf(\Swift_Message::class, $emailMessage);

$body     = $emailMessage->getBody();
$expected = \file_get_contents(__DIR__ . '/../data/RestController/ServiceController/you_message_body.html');
// do not forget to replace dynamic data in the snapshot.
$body     = \preg_replace('/cid:[a-z0-9]{32}/ui', 'cid_here', $body);
$this->assertEquals($expected, $body);

Upvotes: 0

geoB
geoB

Reputation: 4716

Took quite a while to find this. Backtracked through assertEmailHeaderSame to find assertEmailHtmlBodyContains then class EmailHtmlBodyContains which has $message->getHtmlBody().

So now my test includes $body = $email->getHtmlBody();, which is a string. Which can be parsed.

Upvotes: 2

Related Questions