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でも旧とほとんど変わらない書き方でテストができます。どれもメールテストではよく使うものだと思いますので、ぜひご活用ください。
メルマガ購読の申し込みはこちらから。