やりたいこと
テストコードで 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()
で取得できます。
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用のコンフィグレーションを指定します。
<?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
を指定します
'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), 'メッセージ');
}
}