
Laravel 11 でお問い合わせ機能を作った時に Mail::raw のテストにハマった話
はじめに
Laravel でフォームの送信完了時にメール送信する機能はよくある実装だと思います。
今回、Laravel 11 でいわゆる “お問い合わせフォーム” を作り、フォーム送信時に
問い合わせしてきたユーザー本人
管理者
の両方に通知メールを送る仕様を実装しました。
どうせならテストもしっかり書こうと思い、Mail::fake() を使って「メール送信回数や内容を検証」する流れを構築したのですが、途中で Mail::raw() による送信が MailFake でうまくカウントされない問題に遭遇したので、Mailable クラスに切り替えた話を書いておきます。
Mail::raw でのメール送信 & テスト
当初は、こんな形で「お問い合わせ本文」を直接組み立てて送っていました:
Mail::raw($text, function ($message) use ($toAddress, $subject) {
$message->to($toAddress)->subject($subject);
});
で、テストコードでは
public function test_inquiry_mail_is_sent()
{
Mail::fake();
// フォーム送信 (Livewire or HTTPリクエスト)
// ...
// 送信回数をチェック
Mail::assertSentCount(1);
}
しかし、実際にはテストでMail::assertSentCount(1) が 0 通扱いになる事象が発生。
ログを見ると 「Mail::raw は呼ばれている」 のに、Fake 上はカウントが増えない…。
Laravel 11 なら MailFake は Mail::raw() に対応しているはずですが、何らかの要因(Parallel Testing や環境差)で一致しないのか、調査に時間を取られてしまいました。
Mailable クラスへ切り替え
結局、公式ドキュメントでも推奨されているように、Mailable クラスで実装し直したらあっさり解決しました。
Mailable クラスはこんな感じ:
// app/Mail/ContactInquiryMailable.php
namespace App\Mail;
use App\Models\Inquiry;
use Illuminate\Mail\Mailable;
class ContactInquiryMailable extends Mailable
{
public function __construct(public Inquiry $inquiry) {}
public function build()
{
return $this->subject("お問い合わせ: {$this->inquiry->subject}")
->view('emails.contact_inquiry');
}
}
呼び出し側は
Mail::to($adminEmails)->send(new ContactInquiryMailable($inquiry));
でOK。
テストでは
Mail::fake();
// ...
Mail::assertSent(ContactInquiryMailable::class, 1);
と書くと、確実に1通だけ送られたかどうかを検証できます。結果、全てのテストが通るようになりました。
まとめ
Mail::raw はシンプルにテキストメールを送れて便利ですが、環境によってはテスト (Mail::fake()) と相性が悪いケースがあるようです。
Mailable クラスにすれば、Mail::assertSent(YourMailable::class) でチェックできるし、HTML/テンプレート対応など拡張も容易。
Laravel のドキュメントも Mailable クラスの使用を推奨しているので、テスト面・保守性を考えるとやはり Mailable 一択だなと改めて感じました。
今回はあくまで “Mail::raw → Mailable に移行してテストが安定した” という話でしたが、もし同じ現象でハマった方の参考になれば幸いです。