Edited at

CakePHP3 でメール送信をテストする

More than 1 year has passed since last update.


やりたいこと

テストコードで Cake\Mailer\Email::send() で送信したメールを確認したいのです。


環境


  • CakePHP version 3.3.13

  • PHPUnit version 5.7.21


ざっくりした仕組み

CakePHPでは Cake\Mailer\Transport\DebugTransport があり、e-mailのトランスポートにこれを指定します。そうすると、Cake\Mailer\Email::send() の戻り値で送信内容を得られます。

しかし、send() の戻り値を使ってテストコードを書くのは違うので、send()の引数に渡された Cake\Mailer\Email を捕まえてテストできる状態にします。


コード


TestableTransport.php

DebugTransport を継承して、send() の引数を staticなプロパティに貯めておきます。貯めたものは sendMailLst() で取得できます。


tests/Mailer/Transport/TestableTransport.php

namespace App\Test\Mailer\Transport;

use Cake\Mailer\Transport\DebugTransport;
use Cake\Mailer\Email;

/**
* Class TestableTransport
* @package Mailer\Transport
*/

class TestableTransport extends DebugTransport
{
/** @var array 送信したメール */
private static $mailList = [];

/**
* 送信済みメールをリセットします。
*/

public static function reset()
{
static::$mailList = [];
}

/**
*
* @return array 送信したメールのリスト
*/

public static function sendMailLst()
{
return static::$mailList;
}

/**
* @inheritdoc
*/

public function send(Email $email)
{
static::$mailList[] = clone $email;
return parent::send($email);
}

}



phpunit.xml.dist

テストクラスにトラスポートを指定するコードを書くことができますが、環境変数 CAKEPHP_ENV にPHPUnit用のコンフィグレーションを指定します。


phpunit.xml.dist

<?xml version="1.0" encoding="UTF-8"?>

<phpunit
colors="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="./tests/bootstrap.php"
>
<php>
<ini name="memory_limit" value="-1"/>
<ini name="apc.enable_cli" value="1"/>

<env name="CAKEPHP_ENV" value="phpunit"/>

</php>

<!-- Add any additional test suites you want to run here -->
<testsuites>
<testsuite name="App Test Suite">
<directory>./tests/TestCase</directory>
</testsuite>
<!-- Add plugin test suites here. -->
</testsuites>

<!-- Setup a listener for fixtures -->
<listeners>
<listener
class="\Cake\TestSuite\Fixture\FixtureInjector"
file="./vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureInjector.php">
<arguments>
<object class="\Cake\TestSuite\Fixture\FixtureManager" />
</arguments>
</listener>
</listeners>

<!-- Ignore vendor tests in code coverage reports -->
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
<directory suffix=".php">./plugins/*/src/</directory>
</whitelist>
</filter>
</phpunit>



config/environments/phpunit.php

PHPUnit用のコンフィグレーションで e-mailのトランスポートに TestableTransport を指定します


config/environments/phpunit.php

    'EmailTransport' => [

'default' => [
'className' => 'App\Test\Mailer\Transport\TestableTransport',
],
],



コントローラー

/**

* メール送信を行うアクション
*/

public function action()
{
// 〜 省略 〜
$mail = new Email();

$mail->from('form@example.co.jp');
$mail->to('to@example.co.jp');
$mail->subject('mail subject');
$mail->send('mail message');
// 〜 省略 〜
}


テストコード

Cake\TestSuite\IntegrationTestCase の子クラスでテスト対象のアクションを実行します。

class XXControllerTest extends IntegrationTestCase

{
public function setUp()
{
parent::setUp();
// 送信したメールをリセットする
TestableTransport::reset();
}

public function testMail()
{
// メール送信を行うアクションを実行する
$this->post('/path/to/action', $params);

// 送信したメールを確認する
$mails = TestableTransport::sendMailLst();
$this->assertEquals( 1 , count($mails), '送信したメールが1件だけ存在する');

/** @var Cake\Mailer\Email $mail */
$mail = $mails[0];
$this->assertEquals( ['form@example.co.jp' =>'form@example.co.jp'] , $mail->from(), '差出人');
$this->assertEquals( ['to@example.co.jp' =>'to@example.co.jp'] , $mail->to(), '送信先');
$this->assertEquals( 'mail subject' , $mail->subject(), '件名');
$this->assertEquals( 'mail message', $mail->message(Email::MESSAGE_TEXT), 'メッセージ');
}
}