[RSpec] ActionMailerのメール送信に関するrequests specまとめ

今や電子メールを持っていて当たり前の時代で、メールを送らないサービスはほとんどありません。

Railsでは、ActionMailerというメールを送る機能が標準で備わっています。

今回はActionMailerを使ったメール送信のテストについて書き方をまとめていきます。

ちなみに、ActionMailerでは同期処理で送るパターンと非同期処理で送るパターンの2種類があるので、2種類を別々にご紹介します。

同期処理と非同期処理

以下のようなメーラーをサンプルに以降、解説していきます。

class MyAwesomeMailer < ApplicationMailer
  def hello_world(email)
    mail(to: email, subject: 'This is Test Mailer!')
  end
end

先ほどご紹介した通り、ActionMailerを使うとき、同期処理非同期処理の2種類の送信方法があります。

同期処理

同期処理をする場合は以下のようにdeliver_nowを呼び出します。

class MailsController < ApplicationController
  def send
    MyAwesomeMailer.hello_world('test@example.com').deliver_now
  end
end

この場合、メール送信が完了するまで、MailsController#sendはクライアントにレスポンスを返しません。

ちょっとしたメールを送りたい場合や、メールを送らないと次に進んではいけない場合はこちらが便利です。

非同期処理

非同期処理の場合は以下のようにdeliver_laterを呼び出します。

class MailsController < ApplicationController
  def send_later
    MyAwesomeMailer.hello_world('test@example.com').deliver_later
  end
end

この場合、メール送信のタスクをキューに貯めて後から処理するので、MailsController#send_laterはクライアントにレスポンスを返したあとで、メールを送信します。

大量のメールを一度に送りたいときや、重いファイルを添付したメールを送りたいときには、ユーザーを待たせることなくストレスフリーな仕様が実現できます。

テストの書き方

メールが送られているかのテスト

# もちろんspec/rails_helper.rbに記述してもOK
include ActiveJob::TestHelper

describe 'MailsController' do
  context '#send' do
    example 'メールが送られる' do
      expect {
        post mails_path
      }.to change { ActionMailer::Base.deliveries.size }.by(1)
    end
  end

  context '#send_later' do
    example 'メールが送られる' do
      expect {
        # perform_enqueued_jobsを利用するにはActiveJob::TestHelperをincludeする必要がある
        perform_enqueued_jobs { post mails_path }
      }.to change { ActionMailer::Base.deliveries.size }.by(1)
    end
  end
end

ActionMailerではテスト環境でメールが送られたとき、ActionMailer::Base.deliveriesの中にメールの内容が格納されます。

もし、うまくdeliveriesの中にメールが格納されていない場合は、config/environments/test.rbにdelivery_method :testの記述があるか確認してみてください。

expect { A }.to change { B }.by( C )

上記のコードは「Aを実行したときBの数値がCだけ増える」という意味です。ここでは、送信済みメールが1つ増えることを確認しています。

もし、N通送る場合は、Cの部分をNにしてあげればOKです。

内容が正しいかのテスト

include ActiveJob::TestHelper

describe 'MailsController' do
  context '#send' do
    example '件名が正しい' do
      post mails_path
      last_mail = ActionMailer::Base.deliveries.last
      expect(last_mail.subject).to eq 'This is Test Mailer!'
    end
  end

  context '#send_later' do
    example '件名が正しい' do
      perform_enqueued_jobs do
        post mails_path
      end
      last_mail = ActionMailer::Base.deliveries.last
      expect(last_mail.subject).to eq 'This is Test Mailer!'
    end
  end
end

deliveriesの中から最後に送られたものを取り出してチェックしています。tobodyもチェックできるので、確認してみてください。

注意

ActionMailer::Base.deliveriesは自動的にリセットされません。リセットするためにはActionMailer::Base.deliveries.clearを明示的に記述する必要があります。

参考リンク