- 更新日: 2014年6月10日
- RSpec
ActionMailer のメール送信テストを RSpec で行う
ActionMailer のテスト方法を調べてテストを書いたので、まとめておきます。RailsGuides の ActionMailer と Testing Your Mailers のページに目を通しておくと、理解しやすいかと思います。
Action Mailer Basics — Ruby on Rails Guides
A Guide to Testing Rails Applications — Ruby on Rails Guides
— 環境 —
rails-4.0.1
rspec-rails-2.14.0
capybara-2.2.0
Action Mailer のテストで確認すべき項目
RailsGuides の Testing Your Mailers で以下のように書いてある。
The goals of testing your mailer classes are to ensure that:・emails are being processed (created and sent)
・the email content is correct (subject, sender, body, etc)
・the right emails are being sent at the right times
以下、翻訳。
1. Eメールが正しく処理されること。(Eメールの作成と送信)
2. Eメールの内容が正しいこと。(表題、送信者のアドレス、本文など)
3. Eメールが正しい時間に送信されること。
読み進めると、unit tests および functional tests で Mailer をテストすると説明してあります。
unit tests:
単体でテストを行い、入力値と出力値を比較して確認する。上記の 1, 2 の項目をテスト。
functional tests:
Mailer を使うコントローラーやモデルが Mailer を正しく操作しているかテストする。正しいEメールが正しい時間に送られたかをテストする。上記 3 の項目をテスト。
Unit Testing
ActionMailer::Base.deliveries に送信済みのメール(メールのオブジェクト)が配列で格納されます。
Action Mailer Basics — Ruby on Rails Guides
Unit テストでは次を確認する。
・Eメール を deliver(送信)した後に、ActionMailer::Base.deliveries が空でないことを確認。
・Eメールの from, to, subject, body などの内容を確認。
以下、RailsGuides の Testing Your Mailers から引用のサンプルコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
require 'test_helper' class UserMailerTest < ActionMailer::TestCase test "invite" do # Send the email, then test that it got queued email = UserMailer.create_invite('me@example.com', 'friend@example.com', Time.now).deliver assert_not ActionMailer::Base.deliveries.empty? # 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 |
environments/test.rb で次のように設定しておくと、Mailer のテストで実際にEメールを送信せずにテストモードで動作します。
config/environments/test.rb
1 |
ActionMailer::Base.delivery_method = :test |
ActionMailer::Base.deliveries 配列は、ActionMailer::TestCase でのみ自動でリセットされる。それ以外のテストでリセットしたい場合は、明示的に ActionMailer::Base.deliveries.clear を呼び出してリセットする必要があります。
Functional Testing
functional tests では、Mailer の deliver メソッドを呼び出し以下を確認する。
・Eメールが正しく delivery list に追加されたかを確認。
・正しいビジネスロジックでEメールが送信されるかを確認。
・正しい時間に送信されるかを確認。
以下、RailsGuides の Testing Your Mailers から引用のサンプルコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
require 'test_helper' class UserControllerTest < ActionController::TestCase test "invite friend" do assert_difference 'ActionMailer::Base.deliveries.size', +1 do post :invite_friend, email: 'friend@example.com' end invite_email = ActionMailer::Base.deliveries.last assert_equal "You have been invited by me@example.com", invite_email.subject assert_equal 'friend@example.com', invite_email.to[0] assert_match(/Hi friend@example.com/, invite_email.body) end end |
ActionMailer::Base.deliveries.size → 送信済みメールの数
ActionMailer::Base.deliveries.last → 最後に送信されたEメール
では前置きが長くなりましたが、以降が自分で書いた RSpec で ActionMailer をテストするコードです。
RSpec で ActionMailer の Unit Test
spec/mailers/user_mailer_spec.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
require "spec_helper" describe UserMailer do before do @subject = I18n.t('user_mailer.registration_confirmation.subject') @from = APP_CONFIG['email']['from'] @login_path = "/users/sign_in" @site_title = "MyHomepage" end after(:all) do ActionMailer::Base.deliveries.clear end describe "registration_confirmation" do describe "when creating mail and before sending it" do let(:new_user) { FactoryGirl.create(:user, email: "example@example.com") } let(:mail) { UserMailer.registration_confirmation(new_user) } # use Base64.decode64 because somehow mail.body is empty let(:mail_body) { mail.body.encoded.split(/\r\n/).map{|i| Base64.decode64(i)}.join } specify { expect(ActionMailer::Base.deliveries).to be_empty } it "renders the headers" do expect(mail.subject).to eq @subject expect(mail.from.first).to eq @from expect(mail.to.first).to eq new_user.email end it "renders the body" do expect(mail_body).to match(/#{@login_path}/) expect(mail_body).to match(/#{@site_title}/) end describe "after sending the mail" do before do mail.deliver end let(:sent_mail) { ActionMailer::Base.deliveries.last } let(:sent_mail_body) { sent_mail.body.encoded.split(/\r\n/).map{|i| Base64.decode64(i)}.join } specify { expect(ActionMailer::Base.deliveries.count).to eq 1 } it "renders the headers" do expect(sent_mail.subject).to eq @subject expect(sent_mail.from.first).to eq @from expect(sent_mail.to.first).to eq new_user.email end it "renders the body" do expect(sent_mail_body).to match(/#{@login_path}/) expect(sent_mail_body).to match(/#{@site_title}/) end end end end end |
メールを作成して送信する前と、送信した後でテストを行っています。送信前は、ActionMailer::Base.deliveries が空であることをチェック、送信後は 1 になっていることを確認しています。
あと、なぜか mail.body が空になりました。 mail.body.encoded だと取得できるのですが、Base64 でエンコードされた文字列なので、デコードする処理を行っています。こうしないと、it “renders the body” do の正規表現マッチャーが使えなかったので。
APP_CONFIG[’email’][‘from’] については以下。
Rails でアプリ固有の設定情報や定数を定義する | EasyRamble
RSpec で ActionMailer の Functional Test
RailsGuides では、Functional test は UserControllerTest に書いてあるので、RSpec の場合もそれに倣うと spec/controllers/user_controller_spec.rb に書けば良いかと思います。私はできるだけ RequestSpec にテストを書くようにしているので、spec/requests/user_regsitration_pages_spec.rb に Capybara でサインアップフォームを埋める動作からテストを書きました。
新規ユーザー登録完了時に、確認メールを送信する UserMailer#registration_confirmation(user) メソッドが実装されているものとします。
Rails4 の ActionMailer でメール送信 | EasyRamble
Deviseでユーザー登録完了時にウェルカムメールを送信する | EasyRamble
spec/requests/user_regsitration_pages_spec.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
describe "UserMailer" do before do @subject = I18n.t('user_mailer.registration_confirmation.subject') @from = APP_CONFIG['email']['from'] @login_path = "/users/sign_in" @site_title = "MyHomepage" end after(:all) do ActionMailer::Base.deliveries.clear end describe "registration_confirmation" do before do visit new_user_registration_path fill_in I18n.t("User.email"), with: "example@example.com" fill_in I18n.t("User.password") , with: "foobarbaz" fill_in I18n.t("User.password_confirmation"), with: "foobarbaz" end it "should send a registration confirmation email to the new user" do expect { click_button t("users.registrations.new.submit") }.to change { ActionMailer::Base.deliveries.count }.by(1) end describe "should have correct information" do let(:sent_mail) { ActionMailer::Base.deliveries.last } let(:sent_mail_body) { sent_mail.body.encoded.split(/\r\n/).map{|i| Base64.decode64(i)}.join } it "renders the headers" do expect(sent_mail.subject).to eq @subject expect(sent_mail.from.first).to eq @from expect(sent_mail.to.first).to eq "example@example.com" end it "renders the body" do expect(sent_mail_body).to match(/#{@login_path}/) expect(sent_mail_body).to match(/#{@site_title}/) end end end end |
Capybara でフォーム入力して ActionMailer 用の RequestSpec を書きました。こんな感じで RSpec で ActionMailer のテストを行えます。他、RSpec での ActionMailer のテストは以下のページが参考になりました。
- – 参考リンク –
- How to test mailers in Rails 3 / 4 with RSpec | Lucas Caton
- How To Write Rspec Mailer Tests – Too Busy Coding…
- Rails3 で コントローラからメールが送信されたか Rspec で確認のメモ – 牌語備忘録 – pygo
- Railsでメール送信系のテストを行うには~email-spec – リア充爆発日記
- Railsで新規にWebサービスを立ち上げる際にやったことまとめ | nanapi TechBlog
- RSpecでActionMailerによるメール送信をテストする方法 – memo.yomukaku.net
- ActionMailer::Base.deliveriesに格納される内容 | もっとクールにプログラミング
- Rails – ActionMailerのテスト – Qiita
- RSpec の関連記事
- FactoryGirlをSpringと共に使う時の注意
- 複数モデルのpost :createをテストするRSpecコード(Controller Spec)
- RSpec3でTime.nowをスタブ化(stub)
- RSpecでJSONによるPOSTリクエストをテスト
- RSpec & Capybara の雑感
- RSpec+Capybaraで同名の複数要素の並び順をテストする
- RSpec3ではrails_helper.rbがrequireされる
- Capybara + Launchy で RSpec テストをブラウザで確認
- CapybaraのwithinをRSpecで使う
- Serverspec(RSpec)のテスト出力に色を付けて見やすくフォーマット
Leave Your Message!