Get content of email sent during command tests
Asked Answered
C

6

7

During my tests I call some commands which send emails. I can display the number of emails sent with the following command:

$output->writeln(
    $spool->flushQueue(
        $this->getContainer()->get('swiftmailer.transport.real')
    )
);

The Symfony2 documentation explains how to get email content by using the profiler during a Web test (also explained here on Stack Overflow), but I don't know how to do the same thing when there is no Web request.

I used the code provided in these links:

<?php

namespace ACME\MyBundle\Tests\Command;

use Liip\FunctionalTestBundle\Test\WebTestCase;

class EmailTest extends WebTestCase
{
    public function tesEmailCommand()
    {
        // load data fixtures

        // http://symfony.com/doc/current/cookbook/email/testing.html
        $client = static::createClient();
        // Enable the profiler for the next request (it does nothing if the profiler is not available)
        $client->enableProfiler();

        /** @var \Symfony\Bundle\FrameworkBundle\Console\Application $application */
        // inherit from the parent class
        $application = clone $this->application;

        $application->add(new EmailCommand());
        $command = $application->find('acme:emails');
        $commandTester = new CommandTester($command);

        $commandTester->execute(array(
            'command' => 'acme:emails'
        ));

        $display = $commandTester->getDisplay();

        $this->assertContains('foo', $display);

        // http://symfony.com/doc/current/cookbook/email/testing.html
        $mailCollector = $client->getProfile()->getCollector('swiftmailer');

        // Check that an email was sent
        $this->assertEquals(1, $mailCollector->getMessageCount());

        $collectedMessages = $mailCollector->getMessages();
        $message = $collectedMessages[0];

        // Asserting email data
        $this->assertInstanceOf('Swift_Message', $message);
        $this->assertEquals(
            'You should see me from the profiler!',
            $message->getBody()
        );
    }
}

It returns this error:

Argument 1 passed to Symfony\Component\HttpKernel\Profiler\Profiler::loadProfileFromResponse() must be an instance of Symfony\Component\HttpFoundation\Response, null given, called in .../vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Client.php on line 72 and defined .../vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Profiler/Profiler.php:81 .../vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Client.php:72 .../src/ACME/MyBundle/Tests/Command/EmailTest.php:94

The error comes from this line:

$mailCollector = $client->getProfile()->getCollector('swiftmailer');

It seems logical because there is no response since there's no request.

I use Symfony 2.8.7.


Here is my Swiftmailer configuration in app/config_test.yml:

swiftmailer:
    disable_delivery: true
    delivery_address: %swiftmailer.delivery_address%
Carmancarmarthen answered 28/7, 2015 at 14:8 Comment(0)
E
9

I've been able to get it working with:

// kernel
$kernel = $this->createKernel();
$kernel->boot();

// container
$container = $kernel->getContainer();

// register swiftmailer logger
$mailer = $container->get('mailer');
$logger = new \Swift_Plugins_MessageLogger();
$mailer->registerPlugin($logger);

And then you can get the message contents with:

foreach ($logger->getMessages() as $message) {
    $subject       = $message->getSubject();
    $plaintextBody = $message->getBody();
    $htmlBody      = $message->getChildren()[0]->getBody();
}
Emeraldemerge answered 16/10, 2016 at 9:34 Comment(1)
Thanks! It worked for me. I added the first block before calling the command.Carmancarmarthen
C
1

I didn't found a solution, instead I made my command more verbose in order to display messages explaining what has been done, which tell me indirectly what is the email content.

Carmancarmarthen answered 15/9, 2015 at 12:1 Comment(0)
R
1

You need to register the plugin into the mailer:

$mailer = static::$container->get('swiftmailer.mailer');
$logger = new \Swift_Plugins_MessageLogger();
$mailer->registerPlugin( $logger );

Then you can loop all the messages sent and check the data you need, for instance:

foreach ( $logger->getMessages() as $message ) {
    echo sprintf( '%s%s',$message->getSubject(), Chr(10) );
    echo sprintf( '%s%s',$message->getBody(), Chr(10) );
}

The key here is having in mind that into the SwiftMail library, the file MailTransport.php dispatches the event "beforeSendPerformed". This event is listened by $logger, instance of Swift_Plugins_MessageLogger. The plugin implements Swift_Events_SendListener interface, one of the methods is getMessages, which contains the content of the messages sent. You can also check swiftmail/swiftmailer number of messages sent in your symfony functional test.

Rattlepate answered 25/5, 2016 at 8:35 Comment(7)
It produces an error: PHP Fatal error: Access to undeclared static property: …\Tests\Command\CommandTest::$container in /…/Tests/Command/CommandTest.php on line 68. I use Symfony 2.8.6 and LiipFunctionalTestBundle (it extends WebTestCase).Carmancarmarthen
You will not have any problem if you extend from KernelTestCase. In case you extend from WebTestCase you can use profiler for checking it: symfony.com/doc/current/cookbook/email/testing.html (please ensure profiler is enabled in your test/dev environment yml files).Rattlepate
Thanks but this link was already in my question. I don't know how to access to the profiler, I don't have any client when I test the command.Carmancarmarthen
Notice that if you extend from WebTestCase, you can instantiate the client by typing $client = static::createClient();Rattlepate
It seems illogical to create a client when no request will be made. I'll have to try this anyway. Thanks.Carmancarmarthen
Instead of extending from WebTestCase you must extend from KernelTestCase. Once done this you can gain access to container by: //start the symfony kernel $kernel = static::createKernel(); $kernel->boot(); //get the DI container self::$container = $kernel->getContainer();Rattlepate
I had no error when I used $mailer = $this->getContainer()->get('swiftmailer.mailer'); …, but $logger->getMessages() is empty. I'll update my question with more details.Carmancarmarthen
F
-1

You can 'fake' a request like so, this might help you out.

$this->getContainer()->enterScope('request');
$request = Request::create('http://yourdomain.com'));
$this->getContainer()->set('request', $request, 'request');
$this->getContainer()->get('router')->getContext()->setHost('http://yourdomain.com');

I have a function which does similar in commands where I use services where a request needs to be present.

Falciform answered 28/7, 2015 at 20:29 Comment(2)
Where should I put this code? What is the link with $client?Carmancarmarthen
I don't understand how to get the content of the email with this code.Carmancarmarthen
S
-1

You can add custom channel for Monolog and send email content to log file.

In config.yml add

monolog:
    channels: ["mm"]
    handlers:
        mm:
            level:    debug
            type:     stream
            path:     "%kernel.logs_dir%/mm.log"
            channels: ["mm"]

and then in command code use:

$logger = $this->get('monolog.logger.mm');
$logger->info($emailContent);
Shaunta answered 29/7, 2015 at 16:47 Comment(1)
I don't have the email content, I'm looking for a way to get it, like Swiftmailer can return the content of sent emails.Carmancarmarthen
F
-1

By default SwiftmailerBundle add message logger plugin

$this->getContainer()
    ->get('swiftmailer.mailer.default.plugin.messagelogger')
    ->getMessages();

You can use it in commands tests.

Facile answered 1/2, 2016 at 16:13 Comment(1)
I tested it and it returned an empty array.Carmancarmarthen

© 2022 - 2024 — McMap. All rights reserved.