見出し画像

Laravel 11 でお問い合わせ機能を作った時に Mail::raw のテストにハマった話

はじめに

Laravel でフォームの送信完了時にメール送信する機能はよくある実装だと思います。
今回、Laravel 11 でいわゆる “お問い合わせフォーム” を作り、フォーム送信時に

  1. 問い合わせしてきたユーザー本人

  2. 管理者

の両方に通知メールを送る仕様を実装しました。
どうせならテストもしっかり書こうと思い、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 に移行してテストが安定した” という話でしたが、もし同じ現象でハマった方の参考になれば幸いです。


いいなと思ったら応援しよう!