Post

RSpecでdeliver_laterのメールをテストする方法

背景

RSpec でメール送信をテストする時、メールを確認できなかった。 色々検索したら、deliver_laterの影響だと気づいた。

解決案

いろいろ試して、上手く行けたのは下記の二つ方法

1. have_enqueued_mail  matcher を使う

一番簡単な方法はhave_enqueued_mail  matcher を使って、メールが実行待ち行列に入っていることを確認する。 先にActiveJob::Base.queue_adapter = :testを追加する必要。

1
2
3
4
5
6
7
8
9
RSpec.describe UserMailer do
  it "matches with enqueued mailer" do
    ActiveJob::Base.queue_adapter = :test
    expect { UserMailer.signup.deliver_later }.to have_enqueued_mail(
      UserMailer,
      :signup,
    )
  end
end

配信予定時間を含めての確認も簡単

1
2
3
4
5
6
7
8
RSpec.describe UserMailer do
  it "matches with enqueued mailer" do
    ActiveJob::Base.queue_adapter = :test
    expect {
      UserMailer.signup.deliver_later(wait_until: Date.tomorrow.noon)
    }.to have_enqueued_mail.at(Date.tomorrow.noon)
  end
end

2. perform_enqueued_jobsメソッドを使う

もしメールの内容まで確認必要だったら、ActiveJob::TestHelperモジュールが用意するperform_enqueued_jobsメソッドを使って、 deliver_laterのメール配信ジョブをすぐ実行させる。

1
2
3
4
5
6
7
8
9
10
11
12
13
RSpec.describe UserMailer do
  include ActiveJob::TestHelper
  it "matches mail result" do
    perform_enqueued_jobs(only: ActionMailer::MailDeliveryJob) do
      expect(UsersMailer.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

先にActiveJob::TestHelperモジュールを include するのが必要。 メールオブジェクトはActionMailer::Base.deliveries.lastを使って取得する。 全てのジョブを有効にすると、効率が悪くなるので、MailDeliveryJobに限定した方良い。


参照( ActionMailer の deliver_later でメール送信する場合のユニットテストの書き方 Have_enqueued_mail matcher perform_enqueued_jobs

This post is licensed under CC BY 4.0 by the author.