Category Archives: CakePHP

Testing Email generation in CakePHP 3

Need to get the Emails generated while running functional tests on your CakePHP 3.2 app? The following recipe might be of interest.

First we need a TestTransport class:

Filename: src/Mailer/Transport/TestTransport.php

namespace App\Mailer\Transport;

use Cake\Core\Configure;
use Cake\Mailer\AbstractTransport;
use Cake\Mailer\Email;

/**
 * Test environment Email Transport
 *
 * @author     Ron Metten <ccct@code-kobold.de>
 * @copyright  Ron Metten <ccct@code-kobold.de>
 */
class TestTransport extends AbstractTransport
{
    /**
     * Send mail.
     *
     * @param \Cake\Mailer\Email $email Cake Email
     * @return array
     */
    public function send(Email $email)
    {
        $headers = $email->getHeaders(['from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'subject']);
        $headers = trim($this->_headersToString($headers));
        $message = trim(implode("\r\n", (array)$email->message()));
        $result = ['headers' => $headers, 'message' => $message];
        Configure::write('test_transport_email.' . $email->subject(), $result);
        return $result;
    }
}

Here we (ab)use the Configure class to store the Email contents.

Add the TestTransport to your config/app.php:

Filename: config/app.php

'EmailTransport' => [
    'default' => [
        'className' => ...,
    ],
    'test' => [
        'className' => 'Test',
    ],
],

Now configure the transport in your test bootstrap file (the one you defined in the phpunit.xml file you use for running your tests, probably tests/bootstrap.php):

Filename: tests/bootstrap.php

use Cake\Mailer\Email;

require dirname(__DIR__) . '/config/bootstrap.php';

/*
 * Change default Email transport to 'test
 */
Email::drop('default');
Email::config('default', [
    'transport' => 'test',
]);

Now generate a test class:

Filename: tests/TestCase/EmailTest.php

namespace App\Test\TestCase;

use Cake\Core\Configure;
use Cake\Mailer\Email;
use Cake\TestSuite\IntegrationTestCase;

class EmailTest extends IntegrationTestCase
{
    public function testMail()
    {
        $subject = 'Emails in functional tests';
        $message = 'A message to CakePHP 3';

        $email = new Email('default');
        $email->from(['me@example.com' => 'Codetalk'])
            ->to('you@example.com')
            ->subject($subject)
            ->send($message);

        $emailContent = Configure::read('test_transport_email.' . $subject);
        $this->assertStringStartsWith($message, $emailContent['message']);
    }
}

The run your test with some Cafe au Lait…