LoginSignup
4
3

More than 5 years have passed since last update.

CakePHP3.7未満のEmailをSMTPなしにユニットテストする方法

Last updated at Posted at 2019-01-16

本稿ではCakePHP3のEmailをSMTPサーバなしにPHPUnitで単体テストする方法を紹介する。

最新のCakeならCake\TestSuite\EmailTraitを使うといい

CakePHPの公式ドキュメントによれば、メールのテストはCake\TestSuite\EmailTraitを使えば良いらしい。しかし、これはCakePHP3.7以降の話だ。3.7は2018/12/09にリリースされたばかりなので、手元の環境ではこれが使えない。

CakePHP3.7未満の対処法

代わりに使えるのはCakePHP3.3.3で追加されたCake\TestSuite\EmailAssertTraitなわけだが、これはEmailのモックを生成するものなので、Emailnewしているコードのテストはできない。

ではテストできないのかというとそうではなく、EmailTransportをSMTPからテスト用のものに差し替えれば、new Emailしているコードもテストすることができる。参照: Cakephp3のTestでメールを送りたくない - Qiita

CakeMailerTestCaseを作る

トランスポートの差し替えは、基本的にsetUp

Email::dropTransport('default');
Email::setConfigTransport('default', $テスト用のTransport);

し、tearDownでもとのトランスポートに戻してあげればいいわけだ。

だが、テストクラスごとに毎回setUptearDownを書くのは面倒なので、次のCakeMailerTestCaseのようなメールテスト機能を持ったテストクラスを作っておくと便利だ:

CakeMailerTestCase.php
<?php

declare(strict_types=1);

namespace App\Test;

use Cake\Mailer\AbstractTransport;
use Cake\Mailer\Email;
use Cake\TestSuite\EmailAssertTrait;
use Cake\TestSuite\TestCase;

abstract class CakeMailerTestCase extends TestCase
{
    use EmailAssertTrait;

    /**
     * @var array
     */
    private $originalEmailTransport;

    public function setUp(): void
    {
        parent::setUp();
        $this->originalEmailTransport = Email::getConfigTransport('default');
        Email::dropTransport('default');
        Email::setConfigTransport('default', $this->getTestTransport());
    }

    public function tearDown(): void
    {
        parent::tearDown();
        Email::dropTransport('default');
        Email::setConfigTransport('default', $this->originalEmailTransport);
    }

    private function getTestTransport(): AbstractTransport
    {
        return new class($this->getSentEmailSetter()) extends AbstractTransport {
            /**
             * @var callable
             */
            private $setSentEmail;

            public function __construct(callable $setSentEmail)
            {
                parent::__construct();
                $this->setSentEmail = $setSentEmail;
            }

            public function send(Email $email): void
            {
                ($this->setSentEmail)($email);
            }
        };
    }

    private function getSentEmailSetter(): callable
    {
        return function (Email $email): void {
            $this->_email = $email;
        };
    }
}

CakeMailerTestCaseを使ったテストコードは次のようになる:

<?php

declare(strict_types=1);

namespace App\Test;

use Cake\Mailer\Email;

final class CakePhpEmailSenderTest extends CakeMailerTestCase
{
    public function test_send_email(): void
    {
        $email = new Email();
        $email->setFrom(['alice@example.com' => 'Alice Brown'])
            ->setTo('bob@example.com')
            ->setSubject('Hi')
            ->send('Hi there.');
        $this->assertEmailFrom('alice@example.com', 'Alice Brown');
        $this->assertEmailTo('bob@example.com');
        $this->assertEmailSubject('Hi');
        $this->assertEmailTextMessageContains('Hi there.');
    }
}

CakeMailerTestCaseCake\TestSuite\EmailAssertTraitを使っているので、assertEmailFromなどのメール系アサーションも使えるようになっている。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3