Symfonyでメールのインテグレーションテストを行う
この記事はSymfony Advent Calender 2018 15日目の記事です。
はじめに
Symfonyでメールのテストを行う方法ですが、公式ドキュメントにもある通りファンクショナルテスト内でプロファイラーを利用したメールのテストは行うことができます。
しかしコントローラのテストまで行うのはややオーバスペックであり、メール単体でテストを行いたいという場合もあると思います。
特にtwigでメール本文を記述していてif文で表示の出し分けを行っていたりすると、実際にメールを送るのは大変ですし簡単にテストコードが書けたらいいなと思うケースはあるのではないかと思います。
本記事はそのようなケースで参考になる内容となっているのではないかと思います。
実は以前似たような記事を書いたことがありました。
以前の記事 okapon-pon.hatenablog.com
しかしながら、MailServiceを継承する方法は制約も多く実用性に欠けますし、もっとスマートな方法があったため改めてご紹介したいと思います。
メールの検証に Swift_Plugins_MessageLogger を使う
まずはじめに、この内容はSymfony3.4ベースです。
Symfonyを使っている方なら既に知っていると思いますが、メールクラスの実態は \Swift_Message
クラスですね。
以降 \Swift_Massge
クラスを Messge クラスと省略させてもらいます。
プロファイラーを利用したテストはできると先程書きましたがプロファイラーは送信したMessageクラスを保持しているので、そこからMessageクラスを取得してきてメールの検証を行うことができます。
そのプロファイラーがどうやってMessageクラスの情報を保持してきているかというと、MessageDataCollector
クラスを通して情報を収集していて DataCollector
はさらに \Swift_Plugins_MessageLogger
クラスから情報を集めます。
Swift_Mailer
クラスからMessageが送信されるタイミングで \Swift_Plugins_MessageLogger
クラスはMessageを保持するようになっています。
これらはSymofnyの環境がdevもしくはtestの場合に行われます。
そのため既にMessageクラスを収集している、この Swift_Plugins_MessageLogger
を利用すれば簡単にメールの検証を行うことができます。
以下では実際の利用コードを示します。
サンプルコード
Symfony3.4
<?php use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class MyMailTest extends WebTestCase { public function testSendMail() { $kernel = self::bootKernel(); $mailer = $kernel->getContainer()->get('mailer'); $logger = $kernel->getContainer()->get('swiftmailer.mailer.default.plugin.messagelogger'); // 実際は実コード側でメール送信を行うが、ここでは分かりやすくテストコード中に記述してます $message = new \Swift_Message(); $message->setTo('to@example.com', 'Aさん'); $message->setFrom('system@example.com'); $message->setBody('本文です。'); $mailer->send($message); // メールを検証します $this->assertSame(1, $logger->countMessages()); /** @var \Swift_Message $message */ $message = $logger->getMessages()[0]; $this->assertSame(['to@example.com' => 'Aさん'], $message->getTo()); $this->assertContains('本文です', $message->getBody()); } }
未確認ですが、Symfony4.1以降であればprivateなサービスクラスも取得できる方法があるため以下のようなコードでも動くと思います。
<?php class MyMailTest extends WebTestCase { public function testSendMail() { self::bootKernel(); // gets the special container that allows fetching private services $container = self::$container; $mailer = $container->get(\Swift_Mailer::class); $logger = $container->get(Swift_Plugins_MessageLogger::class); // ...略 } }
参考
New in Symfony 4.1: Simpler service testing (Symfony Blog)
このサンプルコードだけ見ればどうということはないのですが、自分はここに到達するまでにわりと遠回りしてしまってました。。。
SwiftMailerBundleのリファレンスにloggingという項目があり、そこからヒントを得ました。 Mailer Configuration Reference (SwiftmailerBundle) (Symfony 3.4 Docs)
まとめ
ファンクショナルテスト(Controllerのテスト)でなくてもサービスコンテナを用いたメールのインテグレーションテストを簡単に書くことができます。
メールのテストを行う場合、ブラウザをポチポチして実メールを送る必要もなくテストコードでさくっと検証できるので開発がはかどります。
以上です。