社内ドキュメントに書いたやつを転載。
課題
deliver_laterを使う場合、Railsガイドのメイラーの機能テストの項にあるような、 ActionMailer::Base.deliveries.last
での検証はできません。
これは、deliver_laterは非同期でのメール送信となるため、即時のメール送信結果の作成が行われないからです。
これに対しては以下の二つのアプローチがあります。
ここでは便利なのでrspecを利用した例を書いていますが、TestHelperを使えばminitestでも同様のテストは可能です。
対応1: ActiveJobのキューを検証する
基本的にこの対応がいいと思います。 コントローラーやモデルでは、キューにいれるまでが責務で、渡された情報をどのようにメールに組み立てるかはActionMailerのテストで賄うべきだからです。
RSpecではマッチャーが用意されており、 have_enqueued_mail
を使うことで簡易に検証できます。
RSpec.describe NotificationsMailer do it "matches with enqueued mailer" do expect { NotificationsMailer.signup.deliver_later }.to have_enqueued_mail(NotificationsMailer, :signup) end end
https://relishapp.com/rspec/rspec-rails/v/5-0/docs/matchers/have-enqueued-mail-matcher
もし、Mailerを呼び出す側でエンキューするときの引数を組み立てるロジックがある場合でも、 .with
で引数を確認するくらいが適切です。 ActionMailer::Base.deliveries.last
の中身の確認はやりすぎ。
rspecでない場合は、ActiveJob::TestHelper を使って、
ActionMailer::MailDeliveryJob
のジョブが入っているかなどを確認するのが良いでしょう。
対応2: 局所的に即時実行にして送信結果を作成する
対応1がおすすめですが、どうしてもメール内容を確認したいという場合はこちらも使えます。
perform_enqueued_jobs
というAPIが用意されているのでそれを使います。
https://edgeapi.rubyonrails.org/classes/ActiveJob/TestHelper.html#method-i-perform_enqueued_jobs
ActiveJob::TestHelper
RSpec.describe NotificationsMailer do it "matches mail result" do perform_enqueued_jobs(only: ActionMailer::MailDeliveryJob) do expect(NotificationsMailer.signup.deliver_later).to change(ActionMailer::Base.deliveries, :count).by(1) mail = ActionMailer::Base.deliveries.last expect( mail.subject).to eq 'mail subject' end end end
局所的に設定変更するやり方の記事もありますが、これだと全部のJobが動いてしまうので悪手です。有効にするJobを選べる、 perform_enqueued_jobs
を使う方が賢い。