メーラーのRSpecテスト
メーラーのRSpecテストでハマったことがあったので、備忘録としてこちらにまとめます。
コードは、以下のようになります。
[app/mailers/user_mailer.rb] class UserMailer < ApplicationMailer # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # # en.user_mailer.reset_password_email.subject # def reset_password_email(user) @user = User.find user.id @url = edit_password_reset_url(@user.reset_password_token) mail(:to => user.email, :subject => "パスワード再発行のお知らせ") end end
[app/views/user_mailer/reset_password_email.html.slim] doctype html html head meta[content="text/html; charset=UTF-8" http-equiv="Content-Type"] body p = "#{@user.name} 様" p パスワード再発行の依頼を受け付けました。 p こちらのリンクからパスワードの再発行を行ってください。 p = link_to nil, @url
[app/views/user_mailer/reset_password_email.text.slim] = "#{@user.name} 様" パスワード再発行の依頼を受け付けました。 こちらのリンクからパスワードの再発行を行ってください。 = @url
[app/mailers/user_mailer_spec.rb] require "rails_helper" RSpec.describe UserMailer, type: :mailer do describe 'パスワードリセットのメール送信の検証' do let(:user) { create(:user) } let(:mail) { UserMailer.reset_password_email(user) } before do user.generate_reset_password_token! mail.deliver_now end context 'メールを送信した時' do it 'ヘッダー情報,ボディ情報が正しい' do expect(mail.subject).to eq 'パスワード再発行のお知らせ' expect(mail.to).to eq [user.email] expect(mail.from).to eq ['from@example.com'] end it 'メール本文が正しい' do expect(mail.html_part.body.to_s).to have_content "#{user.name} 様" expect(mail.html_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' expect(mail.html_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。' expect(mail.html_part.body.to_s).to have_content "http://localhost:3000/password_resets/#{user.reset_password_token}/edit" expect(mail.text_part.body.to_s).to have_content "#{user.name} 様" expect(mail.text_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' expect(mail.text_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。' expect(mail.text_part.body.to_s).to have_content "http://localhost:3000/password_resets/#{user.reset_password_token}/edit" end end end end
上記のテストを実行すると以下のようなエラーになります。
higmonta@higuchiyuunoMBP fishing_cooking % bundle exec rspec spec/mailers/user_mailer_spec.rb DEPRECATION WARNING: Initialization autoloaded the constants ActionText::ContentHelper and ActionText::TagHelper. Being able to do this is deprecated. Autoloading during initialization is going to be an error condition in future versions of Rails. Reloading does not reboot the application, and therefore code executed during initialization does not run again. So, if you reload ActionText::ContentHelper, for example, the expected changes won't be reflected in that stale Module object. These autoloaded constants have been unloaded. Please, check the "Autoloading and Reloading Constants" guide for solutions. (called from <top (required)> at /Users/higmonta/workspace/fishing_cooking/config/environment.rb:5) UserMailer パスワードリセットのメール送信の検証 メールを送信した時 ヘッダー情報,ボディ情報が正しい メール本文が正しい (FAILED - 1) Failures: 1) UserMailer パスワードリセットのメール送信の検証 メールを送信した時 メール本文が正しい Failure/Error: expect(mail.text_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' expected to find text "パスワード再発行の依頼を受け付けました。" in "大野 莉子 様。パスワード再発行の依頼を受け付けました>。こちらのリンクからパスワードの再発行を行ってください>http://localhost:3000/password_resets/hf6PPCGouyRVPfAzi3i3/edit" # ./spec/mailers/user_mailer_spec.rb:25:in `block (4 levels) in <top (required)>' Finished in 0.36584 seconds (files took 3.99 seconds to load) 2 examples, 1 failure Failed examples: rspec ./spec/mailers/user_mailer_spec.rb:19 # UserMailer パスワードリセットのメール送信の検証 メールを送信した時 メール本文が正しい
デバッグして確認してみます。
higmonta@higuchiyuunoMBP fishing_cooking % bundle exec rspec spec/mailers/user_mailer_spec.rb DEPRECATION WARNING: Initialization autoloaded the constants ActionText::ContentHelper and ActionText::TagHelper. Being able to do this is deprecated. Autoloading during initialization is going to be an error condition in future versions of Rails. Reloading does not reboot the application, and therefore code executed during initialization does not run again. So, if you reload ActionText::ContentHelper, for example, the expected changes won't be reflected in that stale Module object. These autoloaded constants have been unloaded. Please, check the "Autoloading and Reloading Constants" guide for solutions. (called from <top (required)> at /Users/higmonta/workspace/fishing_cooking/config/environment.rb:5) UserMailer パスワードリセットのメール送信の検証 メールを送信した時 ヘッダー情報,ボディ情報が正しい ^A^BFrom:^A^B /Users/higmonta/workspace/fishing_cooking/spec/mailers/user_mailer_spec.rb:24 : 19: it 'メール本文が正しい' do 20: expect(mail.html_part.body.to_s).to have_content "#{user.name} 様" 21: expect(mail.html_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' 22: expect(mail.html_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。' 23: expect(mail.html_part.body.to_s).to have_content "http://localhost:3000/password_resets/#{user.reset_password_token}/edit" => 24: binding.pry 25: expect(mail.text_part.body.to_s).to have_content "#{user.name} 様" 26: expect(mail.text_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' 27: expect(mail.text_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。' 28: expect(mail.text_part.body.to_s).to have_content "http://localhost:3000/password_resets/#{user.reset_password_token}/edit" 29: end [1] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail.html_part.body.to_s => "<!DOCTYPE html><html><head><meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\" /><style> /* Email styles need to be inline */ </style></head><body><!DOCTYPE html><html><head><meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\" /></head><body><p>村上 美穂 様</p><p>パスワード再発行の依頼を受け付けました。</p><p>こちらのリンクからパスワードの再発行を行ってください。</p><p><a href=\"http://localhost:3000/password_resets/BtLzvHUukixrNqAQoJJx/edit\">http://localhost:3000/password_resets/BtLzvHUukixrNqAQoJJx/edit</a></p></body></html></body></html>" [2] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail.text_part.body.to_s => "村上 美穂 様<パスワード再発行の依頼を受け付けました>。</パスワード再発行の依頼を受け付けました><こちらのリンクからパスワードの再発行を行ってください>。</こちらのリンクからパスワードの再発行を行ってください>http://localhost:3000/password_resets/BtLzvHUukixrNqAQoJJx/edit" [3] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content "#{user.name} 様" => true [4] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました [4] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' RSpec::Expectations::ExpectationNotMetError: expected to find text "パスワード再発行の依頼を受け付けました。" in "村上 美穂 様。パスワード再発行の依頼を受け付けました>。こちらのリンクからパスワードの再発行を行ってください>http://localhost:3000/password_resets/BtLzvHUukixrNqAQoJJx/edit" from /Users/higmonta/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-support-3.11.0/lib/rspec/support.rb:102:in `block in <module:Support>' [5] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を [5] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。' RSpec::Expectations::ExpectationNotMetError: expected to find text "こちらのリンクからパスワードの再発行を行ってください。" in "村上 美穂 様。パスワード再発行の依頼を受け付けました>。こちらのリンクからパスワードの再発行を行ってください>http://localhost:3000/password_resets/BtLzvHUukixrNqAQoJJx/edit" from /Users/higmonta/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-support-3.11.0/lib/rspec/support.rb:102:in `block in <module:Support>' [6] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content "http://localhost:3000/password_resets/#{user.reset_password_token}/edit"n}/edit" => true
下記2つだけがエラーになっています。(ユーザー名の文章部分とURLの部分はエラーになっていません。)
expect(mail.text_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。'
expect(mail.text_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。'
下記のように期待している文章が入っているのでエラーにならないはずだが。。。。
[2] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail.text_part.body.to_s => "村上 美穂 様<パスワード再発行の依頼を受け付けました>。</パスワード再発行の依頼を受け付けました><こちらのリンクからパスワードの再発行を行ってください>。</こちらのリンクからパスワードの再発行を行ってください>http://localhost:3000/password_resets/BtLzvHUukixrNqAQoJJx/edit"
上記を確認するとエラーになっている文章の部分だけ<
や</
や>
が入っており、これに問題がありそうと考えました。
そもそも、なぜここの文章だけが<
や</
や>
が入っているのか再度メールのビューを確認しました。
[app/views/user_mailer/reset_password_email.text.slim] = "#{@user.name} 様" パスワード再発行の依頼を受け付けました。 ① こちらのリンクからパスワードの再発行を行ってください。 ② = @url
そもそもこれってslim形式→text形式に変換されるファイルなはずなのだが、①、②にhtmlタグを記載せずに文章だけ書いてしまっている。(textのように記載してしまっている。)
下記のように編集したところ、テストが通りました。
p = "#{@user.name} 様" p パスワード再発行の依頼を受け付けました。 p こちらのリンクからパスワードの再発行を行ってください。 p = @url
デバッグで確認したら以下のようになり、<
や</
や>
が無くなっていました。
higmonta@higuchiyuunoMBP fishing_cooking % bundle exec rspec spec/mailers/user_mailer_spec.rb DEPRECATION WARNING: Initialization autoloaded the constants ActionText::ContentHelper and ActionText::TagHelper. Being able to do this is deprecated. Autoloading during initialization is going to be an error condition in future versions of Rails. Reloading does not reboot the application, and therefore code executed during initialization does not run again. So, if you reload ActionText::ContentHelper, for example, the expected changes won't be reflected in that stale Module object. These autoloaded constants have been unloaded. Please, check the "Autoloading and Reloading Constants" guide for solutions. (called from <top (required)> at /Users/higmonta/workspace/fishing_cooking/config/environment.rb:5) UserMailer パスワードリセットのメール送信の検証 メールを送信した時 ヘッダー情報,ボディ情報が正しい ^A^BFrom:^A^B /Users/higmonta/workspace/fishing_cooking/spec/mailers/user_mailer_spec.rb:24 : 19: it 'メール本文が正しい' do 20: expect(mail.html_part.body.to_s).to have_content "#{user.name} 様" 21: expect(mail.html_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' 22: expect(mail.html_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。' 23: expect(mail.html_part.body.to_s).to have_content "http://localhost:3000/password_resets/#{user.reset_password_token}/edit" => 24: binding.pry 25: expect(mail.text_part.body.to_s).to have_content "#{user.name} 様" 26: expect(mail.text_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' 27: expect(mail.text_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。' 28: expect(mail.text_part.body.to_s).to have_content "http://localhost:3000/password_resets/#{user.reset_password_token}/edit" 29: end [1] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail.text_part.body.to_s => "<p>酒井 仁 様</p><p>パスワード再発行の依頼を受け付けました。</p><p>こちらのリンクからパスワードの再発行を行ってください。</p><p>http://localhost:3000/password_resets/mwxXk4h9vCMZfHwvXnRz/edit</p>" [2] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content "#{user.name} 様"ame} 様" => true [3] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました [3] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' => true [4] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を [4] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。' => true [5] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> expect(mail.text_part.body.to_s).to have_content "http://localhost:3000/password_resets/#{user.reset_password_token}/edit" => true
※ポイント
mail.html_part.body.to_s
:マルチパートメールにしている場合にhtml形式のメール本文の検証ができる。
mail.text_part.body.to_s
:マルチパートメールにしている場合にtext形式のメール本文の検証ができる。
それぞれのメソッドがどのような動きになっているのか気になったので、デバッグして確認した結果が以下になります。
higmonta@higuchiyuunoMBP fishing_cooking % bundle exec rspec spec/mailers/user_mailer_spec.rb DEPRECATION WARNING: Initialization autoloaded the constants ActionText::ContentHelper and ActionText::TagHelper. Being able to do this is deprecated. Autoloading during initialization is going to be an error condition in future versions of Rails. Reloading does not reboot the application, and therefore code executed during initialization does not run again. So, if you reload ActionText::ContentHelper, for example, the expected changes won't be reflected in that stale Module object. These autoloaded constants have been unloaded. Please, check the "Autoloading and Reloading Constants" guide for solutions. (called from <top (required)> at /Users/higmonta/workspace/fishing_cooking/config/environment.rb:5) UserMailer パスワードリセットのメール送信の検証 メールを送信した時 ヘッダー情報,ボディ情報が正しい ^A^BFrom:^A^B /Users/higmonta/workspace/fishing_cooking/spec/mailers/user_mailer_spec.rb:24 : 19: it 'メール本文が正しい' do 20: expect(mail.html_part.body.to_s).to have_content "#{user.name} 様" 21: expect(mail.html_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' 22: expect(mail.html_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。' 23: expect(mail.html_part.body.to_s).to have_content "http://localhost:3000/password_resets/#{user.reset_password_token}/edit" => 24: binding.pry 25: expect(mail.text_part.body.to_s).to have_content "#{user.name} 様" 26: expect(mail.text_part.body.to_s).to have_content 'パスワード再発行の依頼を受け付けました。' 27: expect(mail.text_part.body.to_s).to have_content 'こちらのリンクからパスワードの再発行を行ってください。' 28: expect(mail.text_part.body.to_s).to have_content "http://localhost:3000/password_resets/#{user.reset_password_token}/edit" 29: end [1] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail => #<Mail::Message:70296108227480, Multipart: true, Headers: <Date: Sun, 11 Sep 2022 04:46:08 +0900>, <From: from@example.com>, <To: delmer_brekke@hudson-hilll.co>, <Message-ID: <631ce980c1322_1665e3fef12436e6c5881e@higuchiyuunoMBP.mail>>, <Subject: パスワード再発行のお知らせ>, <Mime-Version: 1.0>, <Content-Type: multipart/alternative; boundary="--==_mimepart_631ce980c0d0a_1665e3fef12436e6c5879e"; charset=UTF-8>, <Content-Transfer-Encoding: 7bit>> [2] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail.html_part => #<Mail::Part:70296108194240, Multipart: false, Headers: <Content-Type: text/html>, <Content-Transfer-Encoding: base64>> [3] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail.html_part.body => #<Mail::Body:0x00007fde2d1d1f88 @ascii_only=false, @boundary=nil, @charset=nil, @encoding="8bit", @epilogue=nil, @part_sort_order=["text/plain", "text/enriched", "text/html"], @parts=[], @preamble=nil, @raw_source= "<!DOCTYPE html><html><head><meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\" /><style> /* Email styles need to be inline */ </style></head><body><!DOCTYPE html><html><head><meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\" /></head><body><p>千葉 一輝 様</p><p>パスワード再発行の依頼を受け付けました。</p><p>こちらのリンクからパスワードの再発行を行ってください。</p><p><a href=\"http://localhost:3000/password_resets/xyD6ASVsM1w6G-gXedCS/edit\">http://localhost:3000/password_resets/xyD6ASVsM1w6G-gXedCS/edit</a></p></body></html></body></html>"> [4] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail.html_part.body.to_s => "<!DOCTYPE html><html><head><meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\" /><style> /* Email styles need to be inline */ </style></head><body><!DOCTYPE html><html><head><meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\" /></head><body><p>千葉 一輝 様</p><p>パスワード再発行の依頼を受け付けました。</p><p>こちらのリンクからパスワードの再発行を行ってください。</p><p><a href=\"http://localhost:3000/password_resets/xyD6ASVsM1w6G-gXedCS/edit\">http://localhost:3000/password_resets/xyD6ASVsM1w6G-gXedCS/edit</a></p></body></html></body></html>" [5] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail.text_part => #<Mail::Part:70296108178900, Multipart: false, Headers: <Content-Type: text/plain>, <Content-Transfer-Encoding: base64>> [6] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail.text_part.body => #<Mail::Body:0x00007fde2d1d0f70 @ascii_only=false, @boundary=nil, @charset=nil, @encoding="8bit", @epilogue=nil, @part_sort_order=["text/plain", "text/enriched", "text/html"], @parts=[], @preamble=nil, @raw_source= "千葉 一輝 様<パスワード再発行の依頼を受け付けました>。</パスワード再発行の依頼を受け付けました><こちらのリンクからパスワードの再発行を行ってください>。</こちらのリンクからパスワードの再発行を行ってください>http://localhost:3000/password_resets/xyD6ASVsM1w6G-gXedCS/edit"> [7] pry(#<RSpec::ExampleGroups::UserMailer::Nested::Nested>)> mail.text_part.body.to_s => "千葉 一輝 様<パスワード再発行の依頼を受け付けました>。</パスワード再発行の依頼を受け付けました><こちらのリンクからパスワードの再発行を行ってください>。</こちらのリンクからパスワードの再発行を行ってください>http://localhost:3000/password_resets/xyD6ASVsM1w6G-gXedCS/edit"
参考記事
RSpecでメーラーのテスト実行時にメールの本文がなぜか空になっている問題を解決する - Qiita
rspec2 - rspec-email - How to get the body text? - Stack Overflow