Pierre de LESPINAY
Pierre de LESPINAY

Reputation: 46188

Test email sending with no controller

My application sends many emails. The official doc tells how to test emails sending with the HTTP client profiler.

But I obviously don't have a Controller for each email I'm sending.

How can I test my emails that are not triggered from a controller ? (I mean not from a request context).


Further detail on my specific case

I'm using a listener to trigger calls to a service which is doing the "email routing"
What I'd like to do here, is fire some events and check what is sent (the email body, title, recipients, ...)

Upvotes: 3

Views: 508

Answers (3)

Pierre de LESPINAY
Pierre de LESPINAY

Reputation: 46188

Following this great post I extended the Swift_MemorySpool

class CountableMemorySpool extends \Swift_MemorySpool implements \Countable
{
    public function count()
    {
        return count($this->messages);
    }

    public function getMessages()
    {
        return $this->messages;
    }
}

Passed it to the mailer used by my service

class MailServiceTest extends KernelTestCase
{
    protected $mailService;
    protected $spool;
// ...
    protected function setUp()
    {
        $this->spool = new CountableMemorySpool();
        $transport = new \Swift_Transport_SpoolTransport(
            new \Swift_Events_SimpleEventDispatcher(),
            $this->spool
        );
        $mailer = new \Swift_Mailer($transport);
        $this->mailService->setMailer($mailer);
    }

And tested

    // ...
    public function testMailSpec()
    {
        // ... Sending the mail ...
        $this->assertCount(1, $this->spool,
            'Expected exactly one email to be sent');
        $msg = $this->spool->getMessages()[0];
        $this->assertArrayHasKey('[email protected]', $msg->getTo(),
            'Wrong recipient');
        $this->assertContains('Tartempion', $msg->getBody(),
            'Expected some string contained in the email body');
    }
}

Upvotes: 2

Dmitry Malyshenko
Dmitry Malyshenko

Reputation: 3051

First of all, the article you're linking at is about functional (e2e) testing, not the unit.

Second, for unit-testing you need to inject some MailerMocker in your Listener (which sends emails) instead of real Mailer.

For example, you may create a simple Mocker like this

class SwiftMailerTester extends \Swift_Mailer
{

    /**
     * @var array[\Swift_Mime_Message]
     *
     */

    protected $sentMessages = array ();

    /**
     * @var \Swift_Mime_Message
     */

    protected $lastSentMessage;

    public function __construct() {

    }
    /**
     * Mocks sending message
     *
     * @param \Swift_Mime_Message $message
     * @param null                $failedRecepients
     *
     * @return int
     */
    public function send(\Swift_Mime_Message $message, &$failedRecepients = null)
    {

        $this->lastSentMessage = $message;
        $this->sentMessages[] = $message;

        return 1;
    }

    /**
     * @return array[\Swift_Mime_Message]
     */
    public function getSentMessages()
    {
        return $this->sentMessages;
    }

    /**
     * @return \Swift_Mime_Message
     */

    public function getLastSentMessage()
    {
        return $this->lastSentMessage;
    }

    /**
     * @return int
     */
    public function getSentMessagesCount()
    {
        return count($this->sentMessages);
    }
}

And then in your unit test access lastSentMessage (or sentMessages) to analyze if Listener had done it's work right.

Upvotes: 1

user4545769
user4545769

Reputation:

You might want to look into setting up the global request context which, although the documentation is for Commands, should help with your situation unless you can provide further, specific, information.

Upvotes: 0

Related Questions