はじめに
CBcloud Advent Calendar 2020 の2日目の記事です。
本記事では、メール送信の単体テストの際、ActionMailer が依存している Mail に含まれている Mail::Matchers
を RSpec のマッチャとして利用する方法を紹介します。
また、比較対象として、以下の2つも同時に記載します。
- Rails Guides に記載されているテスト方法
- RSpec のドキュメントに記載されているテスト方法
最後に、Mail::Matchers
を使った例の紹介と、実際に動くサンプルコードを添付します。
動作確認環境
- ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19]
- actionmailer (6.0.3.4)
- rspec (3.10.0)
テストコード
Rails Guides に記載されているテスト方法
require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
test "invite" do
# Create the email and store it for further assertions
email = UserMailer.create_invite('me@example.com',
'friend@example.com', Time.now)
# Send the email, then test that it got queued
assert_emails 1 do
email.deliver_now
end
# Test the body of the sent email contains what we expect it to
assert_equal ['me@example.com'], email.from
assert_equal ['friend@example.com'], email.to
assert_equal 'You have been invited by me@example.com', email.subject
assert_equal read_fixture('invite').join, email.body.to_s
end
end
RSpec のドキュメントに記載されているテスト方法
require "rails_helper"
RSpec.describe NotificationsMailer, :type => :mailer do
describe "notify" do
let(:mail) { NotificationsMailer.signup }
it "renders the headers" do
expect(mail.subject).to eq("Signup")
expect(mail.to).to eq(["to@example.org"])
expect(mail.from).to eq(["from@example.com"])
end
it "renders the body" do
expect(mail.body.encoded).to match("Hi")
end
end
end
Mail::Matchers を使ったテスト方法
ActionMailer の依存で Mail gem は既にインストール済みなので、導入手順は簡単です。
- RSpec の設定で、
Mail::Matchers
を include する - ActionMailer のテストで
Mail::Matchers
が提供するマッチャを使う
以下が実際に Mail::Matchers
を使って書いたコードです。
比較のため、最初のコンテキストでは先述した RSpec のドキュメントに記載されたサンプルを踏襲したコードを実装しています。
RSpec.configure do |config|
config.include Mail::Matchers, type: :mailer
end
RSpec.describe NotificationsMailer, type: :mailer do
before do
ActionMailer::Base.deliveries.clear
end
describe '#signup' do
# @see: https://relishapp.com/rspec/rspec-rails/v/3-9/docs/mailer-specs/mailer-spec
context 'when using RSpec mailer examples' do
subject(:mail) { described_class.signup }
it 'renders the headers' do
expect(mail.subject).to eq('Signup')
expect(mail.to).to eq(['to@example.org'])
expect(mail.from).to eq(['from@example.com'])
end
it 'renders the body' do
expect(mail.body.encoded).to match('Hi')
end
it 'sends the mail' do
expect { mail.deliver_now }.to change { ActionMailer::Base.deliveries.count }
end
end
# @see: https://github.com/mikel/mail#using-mail-with-testing-or-specing-libraries
context 'when using Mail::Matchers' do
subject(:mail) { described_class.signup.deliver_now }
it { is_expected.to have_sent_email }
it { is_expected.to have_sent_email.from('from@example.com') }
it { is_expected.to have_sent_email.to('to@example.org') }
it { is_expected.to have_sent_email.with_subject('Signup') }
it { is_expected.to have_sent_email.with_body('Hi') }
end
end
end
メール送信後のマッチャが全て have_sent_email
に集約されているのがわかりますね。
ここで紹介したもの以外にも、添付ファイルに対するマッチャなど、メールのテストに必要なものが一通り揃っているので、ぜひドキュメントを確認して使ってみてください。
https://github.com/mikel/mail#using-mail-with-testing-or-specing-libraries
最後に
ここまで読んで、実際に自分で試してみたいと思われた方のために、動くサンプルコードを Gist で公開しています。
https://gist.github.com/tomohiro/dfa7362cd066dd87f25f40bdd6856513
余談
個人的には --format documentation
で出力したときの表現が冗長に感じています。