LoginSignup
12
0

More than 3 years have passed since last update.

【Rails】ActionMailer のテストで Mail::Matchers を使う

Last updated at Posted at 2020-12-04

はじめに

CBcloud Advent Calendar 2020 の2日目の記事です。

本記事では、メール送信の単体テストの際、ActionMailer が依存している Mail に含まれている Mail::Matchers を RSpec のマッチャとして利用する方法を紹介します。

また、比較対象として、以下の2つも同時に記載します。

  1. Rails Guides に記載されているテスト方法
  2. 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

実行例:
mail_matcher_example.png

余談

個人的には --format documentation で出力したときの表現が冗長に感じています。

参考資料

12
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
0