LaravelのMailableはメール送信に便利なクラスですが、テストでも同様にMailableをアサートするための便利なメソッドが用意されています。この記事では、Laravel10系の新しいMailableを使ったメール送信テストの書き方をご紹介します。
新Mailableでのメール送信についてはこちらの記事をご覧ください。
テスト対象
テスト対象は以下のクラスです。送信先のメールアドレスを受け取って送信するシンプルなものです。
class Message extends Model { public static function sendMail($email) { Mail::to($email)->send(new BuildMail); } }
send
の引数に渡しているBuildMail
がMailableを継承しているクラスとなり、以下のように定義しています。
namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Attachment; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\SerializesModels; class BuildMail extends Mailable { use Queueable, SerializesModels; /** * Create a new message instance. */ public function __construct() { // } /** * Get the message envelope. */ public function envelope(): Envelope { return new Envelope( subject: 'Test Mail', from: 'from@example.com', ); } /** * Get the message content definition. */ public function content(): Content { return new Content( html: 'emails.test', ); } /** * Get the attachments for the message. * * @return array<int, \Illuminate\Mail\Mailables\Attachment> */ public function attachments(): array { return [ Attachment::fromPath( storage_path('app/img/testmail.jpg') ), ]; } }
メールの送信回数をテスト
まず、メールが1回送信されたことをテストしてみましょう。テストコードは以下のようになります。
public function sendMailTest() { Mail::fake(); //テスト対象の関数を実行 Message::sendMail('to@example.com'); Mail::assertSent(BuildMail::class, 1); }
最初にMail::fake()
を使用しています。テストでは実際にメールを送信する必要はないので、メール送信をモック化し実際に送信されることを防いでいます。
Mail::assertSent()
では、メールが送信されたかどうか、またその回数をアサートしています。第一引数がメール送信クラス、第二引数が期待する送信回数です。今回は1回送信されることを確認するので、1としています。
これでテストを実行すると、無事OKとなりました。
% vendor/bin/phpunit --filter sendMailTest PHPUnit 10.4.1 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 00:00.447, Memory: 26.00 MB OK (1 test, 1 assertions)
もし期待と異なりメールが2通送信された場合、以下のエラーが返ってきます。エラーメッセージにも送信数が違う旨が書いてあるので分かりやすいですね。
The expected [App\Mail\BuildMail] mailable was sent 2 times instead of 1 times. Failed asserting that 2 is identical to 1.
件名・宛先をテスト
件名や宛先など、より詳しく送信メールの情報をアサートしたい場合も同様にMail::assertSent()
を使います。その際、以下のように第二引数のクロージャーでBuildMailクラスのインスタンスである$mail
変数を受け取る必要があります。
Mail::assertSent(BuildMail::class, function ($mail) { return $mail->hasTo('to@example.com') && $mail->hasFrom('from@example.com') && $mail->hasSubject('Test Mail'); });
hasTo
で送信先のメールアドレスを、hasFrom
で送信元のメールアドレスを、またhasSubject
でメールの件名をそれぞれアサートしています。新・旧Mailableで関数名も同じですね。
テストを実行してみると、こちらもOKが出ました。
% vendor/bin/phpunit --filter sendMailTest PHPUnit 10.4.1 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 00:00.353, Memory: 26.00 MB OK (1 test, 1 assertions)
このテストの際、新Mailableになって便利になったと感じたことがあります。
旧Mailableでは、build
メソッドを使用しsubject
やfrom
を指定しているケースも少なくないと思います。その場合、テストコード内でもbuild
の記述が必要でした。以下のようにです。
Mail::assertSent(BuildMail::class, function ($mail) { $mail->build(); return $mail->hasTo('to@example.com') &&.... });
ですが、build
を使用せず新Mailableで提供されるようになったenvelope
やcontent
を使用していれば、特に何も気にしなくともアサートは成功します。これはテストを書く立場として少し嬉しいです。
添付ファイルをテスト
次に、添付ファイルが期待通りか確認します。
BuildMailの定義でも使用していたAttachment
クラスを使うので、use
をお忘れなく。
... use Illuminate\Mail\Mailables\Attachment; ... public function sendMailTest() { ...//関数の実行など Mail::assertSent(BuildMail::class, function ($mail) { return $mail->hasAttachment( Attachment::fromPath(storage_path('app/img/testmail.jpg')), ); }); }
複数のメールを確認
1回で複数件送信されるメールのアサートも簡単です。以下は、計3通のメールが送信されたか、その宛先がそれぞれ期待通りかをアサートする場合のテストコードです。
Mail::assertSent(BuildMail::class, 3); Mail::assertSent(BuildMail::class, function ($mail) { return $mail->hasTo('member1@example.com'); }); Mail::assertSent(BuildMail::class, function ($mail) { return $mail->hasTo('member2@example.com'); }); Mail::assertSent(BuildMail::class, function ($mail) { return $mail->hasTo('member3@example.com'); });
このように、それぞれ順番にアサートを記述するだけで大丈夫です。こちらも新・旧Mailableで特に変わりはありません。
以上のように、新しいMailableでも旧とほとんど変わらない書き方でテストができます。どれもメールテストではよく使うものだと思いますので、ぜひご活用ください。
メルマガ購読の申し込みはこちらから。