【问题标题】:Testing ActionMailer multipart emails(text and html version) with RSpec使用 RSpec 测试 ActionMailer 多部分电子邮件(文本和 html 版本)
【发布时间】:2011-06-27 00:13:05
【问题描述】:

我目前正在使用 RSpec 测试我的邮件程序,但我已经开始按照 Rails 指南中所述设置多部分电子邮件:http://guides.rubyonrails.org/action_mailer_basics.html#sending-multipart-emails

我有文本和 html 格式的邮件模板,但看起来我的测试只是检查 HTML 部分。有没有办法单独检查文本模板?

是否只检查 HTML 视图,因为它是默认顺序中的第一个?

【问题讨论】:

    标签: ruby ruby-on-rails-3 rspec actionmailer multipart


    【解决方案1】:

    作为补充,nilmethod 的出色答案,您可以通过使用共享示例组测试文本和 html 版本来清理规范:

    spec_helper.rb

    def get_message_part (mail, content_type)
      mail.body.parts.find { |p| p.content_type.match content_type }.body.raw_source
    end
    
    shared_examples_for "multipart email" do
      it "generates a multipart message (plain text and html)" do
        mail.body.parts.length.should eq(2)
        mail.body.parts.collect(&:content_type).should == ["text/plain; charset=UTF-8", "text/html; charset=UTF-8"]
      end
    end
    

    your_email_spec.rb

    let(:mail) { YourMailer.action }
    
    shared_examples_for "your email content" do
      it "has some content" do
        part.should include("the content")
      end
    end
    
    it_behaves_like "multipart email"
    
    describe "text version" do
      it_behaves_like "your email content" do
        let(:part) { get_message_part(mail, /plain/) }
      end
    end
    
    describe "html version" do
      it_behaves_like "your email content" do
        let(:part) { get_message_part(mail, /html/) }
      end
    end
    

    【讨论】:

    • 您可以使用mail.text_part.body.raw_source & mail.html_part.body.raw_source 代替get_message_part
    【解决方案2】:

    这可以用正则表达式进行测试。

    在 HTML 部分查找内容(在此之后使用 #should 进行匹配):

    mail.body.parts.find {|p| p.content_type.match /html/}.body.raw_source
    

    在纯文本部分查找内容(在此之后使用#should 进行匹配):

    mail.body.parts.find {|p| p.content_type.match /plain/}.body.raw_source
    

    检查它是否确实在生成多部分消息:

    it "generates a multipart message (plain text and html)" do
      mail.body.parts.length.should == 2
      mail.body.parts.collect(&:content_type).should == ["text/html; charset=UTF-8", "text/plain; charset=UTF-8"]
    end 
    

    【讨论】:

    • 您可以使用mail.text_partmail.html_part 代替mail.body.parts.find { ... }
    • text_parthtml_part 更可取,因为即使它们在其他部分中,它们也能找到相关部分:它们使用 mail.all_parts 而不仅仅是 mail.parts(并且 all_parts 递归获取每个部分中的部分)
    【解决方案3】:

    为了让事情更简单,你可以使用

    message.text_part    and
    message.html_part
    

    找到相应的部分。这甚至适用于带有附件的结构化多部分/替代消息。 (在 Ruby 1.9.3 和 Rails 3.0.14 上测试。)

    这些方法采用某种启发式方法来查找相应的邮件部分,因此如果您的邮件有多个文本部分(例如,Apple Mail 创建它们时),它可能无法做“正确的事情”。

    这会将上述方法更改为

    def body_should_match_regex(mail, regex)
     if mail.multipart?
      ["text", "html"].each do |part|
       mail.send("#{part}_part").body.raw_source.should match(regex)
      end
     else
      mail.body.raw_source.should match(regex)
     end
    end
    

    它适用于纯文本(非多部分)消息和多部分消息,并针对特定的正则表达式测试所有消息正文。

    现在,有没有志愿者可以用它制作一个“真正的”RSpec 匹配器? :) 类似

    @mail.bodies_should_match /foobar/
    

    会好很多...

    【讨论】:

      【解决方案4】:

      如果您的电子邮件有附件,则文本和 html 部分将最终放置在 multipart/alternative 部分中。这在Sending Emails with Attachments in the Rails 3 Guide 下注明。

      为了处理这个问题,我首先将上面的get_message_part 方法简化为:

      def get_message_part(mail, content_type)
        mail.body.parts.find { |p| p.content_type.match content_type }
      end
      

      然后在我的测试中:

      multipart = get_message_part(email, /multipart/)
      
      html = get_message_part(multipart, /html/)
      html_body = html.body.raw_source
      
      assert_match 'some string', html_body
      

      【讨论】:

        【解决方案5】:

        我已经这样做了,我发现它更简单,因为除了样式和标记之外,两封电子邮件的内容都将相似。

        context 'When there are no devices' do
          it 'makes sure both HTML and text version emails are sent' do
            expect(mail.body.parts.count).to eq(2)
            # You can even make sure the types of the part are `html` and `text`
          end
        
          it 'does not list any lockboxes to be removed in both types emails' do
            mail.body.parts.each do |part|
              expect(part.body).to include('No devices to remove')
            end
          end
        end
        

        【讨论】:

        • 这对我有用,我所做的唯一更改是使用 all 匹配器,例如expect(mail.body.parts.map(&:body)).to all include('No devices to remove')
        猜你喜欢
        • 2013-07-13
        • 2013-02-24
        • 2011-09-24
        • 2015-06-09
        • 1970-01-01
        • 2018-09-12
        • 2011-05-20
        • 2012-04-24
        • 2014-10-22
        相关资源
        最近更新 更多