セキュリティ系の勉強、その他開発メモとか雑談. GithubはUnity触っていた頃ものがメイン Twitterフォローもよろしくです

【RSpec】ActionMailer deliver_now が呼ばれる事をテストする

やりたかった事

HogeMailerがあり、HogeDeliveryインスタンスメソッド(send_email)内でHogeMailerを使ってメールを飛ばしていました。

私は今HogeDeliveryのモデルテストを書いていたのでsend_emailのテストを書こうとしました。しかし、ActionMailer::Base.deliveries.count等は使えません(なぜかは知らないので聞かないで) ので、実際にメールが送信できたのか別手段でテストしようと思ったのが始まり。

前提

class HogeMalier < ActionMailer::Base
  def send_hoge
    mail(to: params[:to], template: params[:template],......)
  end
end


class HogeDelivery < ActiveRecord::Base
  def send_email
    # 色々処理する
    # Userクラスからuserを引っ張ったり
    ...
    HogeMailer.with(toとかsubjectとか).send_hoge.deliver_now # ここがdeliver_laterの場合もある
  end
end

deliver_laterの場合

deliver_laterActiveJobを利用して、非同期的にメールを送信します。have_enqueued_jobでメール送信ジョブが登録されたことを確認できます。同時にenqueued_jobsからはその情報を取得することができるので、中身をテストしたい場合はこちらを使います。

describe 'HogeDelivery', type: :model do
  subject{send_mail}(hoge_delivery.send_email) # 引数に送信先の情報を渡す実装ならここ変更
  let!(:hoge_delivery){create(:hoge_delivery)}
  let!(:user){create(:user)}

  it 'enqueue a job' do
    expect { send_mail }.to have_enqueued_job.on_queue("mailers") # ジョブが登録される
  end
  it 'use user name for "to" value' do
    send_mail
    expect(enqueued_jobs[0][:args][3]["to"]).to eq user.name # ジョブから値を引っ張ってくる
  end
end

deliver_nowの場合

deliver_nowActiveJobを使用せずに同期的に送信するため、上の方法では送信できているのか確認できません。

しかし、よくよく考えるとこのHogeDeliveryモデルテストで担保すべきは、deliver_nowがしっかり呼ばれることです。deliver_nowはあくまでHogeMailerの責務として考えた場合、スタブを利用してメソッドが呼ばれることのみをテストすれば良さそうです。

describe 'HogeDelivery', type: :model do
  subject{send_mail}(hoge_delivery.send_email) # 引数に送信先の情報を渡す実装ならここ変更
  let!(:hoge_delivery){create(:hoge_delivery)}
  let!(:user){create(:user)}

  it "call #deliver_later" do
    message_delivery = instance_double(ActionMailer::MessageDelivery)
    allow(HogeMailer).to receive_message_chain(:with, :send_hoge).
                           with(to: to, subject: ..とか色々).with(no_args).
                           and_return(message_delivery)

    expect(message_delivery).to receive(:deliver_later
    send_mail
  end
end

しかし、send_email内ではUserクラスから情報を引き出してきたりと、テストすべき部分はあります。そのため、deliver_laterを使っているのであれば、上のような期待した値が格納されているかテストするのも、大事かと思います。

余談

スタブは、メソッドを実行させずに欲しい値を取得するもの、モックは指定のメソッドが実行されたかどうかをチェックするためな感じだそうです。 スタブはallow、モックはexpect。まぁ詳しくは参考サイトを見てみてください。

参考サイト

tonirib.github.io

qiita.com

techracho.bpsinc.jp