💬
Laravelのメールのテストでの詰まった話
laravelのメールのテストで、宛先を指定するメールのテストが通らない問題で詰まったので、その話をします。
はじめに
GETで/mail/sendにアクセスしたら、メールを送信する仕様になっています。
TestMailというMailableクラスを作成して、その中で宛先や件名の設定をしています。
class TestMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct($name, $email)
{
$this->name = $name;
$this->email = $email;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->to($this->email)//宛先の指定←ここに注目
->subject('テストタイトル')//件名の設定
->view('test.mail')
->with([
'name' => $this->name,
]);
}
}
コントローラーは以下の感じです。
class MailController extends Controller
{
public function send(Request $request)
{
$name = 'テスト ユーザー';
$email = 'test@example.com';
Mail::send(new TestMail($name, $email));
return view('welcome');
}
}
テストを書いてみる
class EmailSendTest extends TestCase
{
use RefreshDatabase;
/** @test */
function メールが送られるかのテスト()
{
$user = User::factory()->create();
Mail::fake(); //メールを本当に送らないようにする
$this->get('/mail/send')
->assertOk();
// メールが送信されたかのテスト(OK)
Mail::assertSent(TestMail::class);
// メールが指定の宛先に送信されたかのテスト(テスト落ちてしまう)
Mail::assertSent(TestMail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
}
}
問題点
上記のテストをすると以下のようなメールの送信されたかのテストは通りました。
//テスト通る
Mail::assertSent(TestMail::class);
しかし、以下の宛先指定のテストが落ちてしまいました。
//テスト通らない
Mail::assertSent(TestMail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
解決方法
試しに以下のように、MailableクラスであるTestMailで宛先を指定しせずに、コントローラーで宛先を指定してみると、、、、
//Mailファサードのtoで宛先を指定するように変更
Mail::to($email)->send(new TestMail($name, $email));
宛先指定のテストが通りました!
//テストが通るようになった!
Mail::assertSent(TestMail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
まとめ
この結果から、Mail::assertSentでのテストは、Mailableクラスをテストしているのではなく、
コントローラーで呼ばれた、Mailファサードのテストをしていたことがわかりました。
よって、メールの細かいテスト(件名やbccや宛先など)をしたい時は、Mailableクラスに情報を書くのではなく、Mailファサードに書きましょう。
追記
テストの内で$mail->build()をすると、Mailableクラスで宛先などを指定していても、テストが通りました。
ご指摘いただきありがとうございました。
Mail::assertSent(TestMail::class, function ($mail) use ($user) {
$mail->build();
return $mail->hasTo($user->email);
});
ご指摘点や不明点などありましたら教えていただけると幸いです。
Discussion
私の Laravel 8.X の環境ですが以下のように
build()
を実行した後であればhasTo()
などが使えています。テストのために書きたいコードが書けないのももどかしいと思うのでよければお試しください。
$mail->build()したら、hasToのテスト通りました!
教えていただきありがとうございました。