51

目前在我的测试中,我做这样的事情来测试电子邮件是否排队发送

assert_difference('ActionMailer::Base.deliveries.size', 1) do       
  get :create_from_spreedly, {:user_id => @logged_in_user.id}
end

但是,如果 ia 控制器操作可以发送两封不同的电子邮件,即如果注册正常则向用户发送一封电子邮件,或者如果出现问题则向管理员发送通知 - 我如何测试实际发送了哪封电子邮件。无论如何,上面的代码都会通过。

4

7 回答 7

82

从 rails 3 ActionMailer::Base.deliveries 开始,是 Mail::Message 的数组。从邮件文档

#  mail['from'] = 'mikel@test.lindsaar.net'
#  mail[:to]    = 'you@test.lindsaar.net'
#  mail.subject 'This is a test email'
#  mail.body    = 'This is a body'
# 
#  mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...

从那里可以很容易地在集成中测试您的邮件

mail = ActionMailer::Base.deliveries.last

assert_equal 'mikel@test.lindsaar.net', mail['from'].to_s

assert_equal 'you@test.lindsaar.net', mail['to'].to_s
于 2010-08-18T23:56:11.757 回答
31

在测试期间使用 ActionMailer 时,所有邮件都放在一个名为deliveries. 您基本上正在做的(并且大部分情况下就足够了)是检查数组中是否存在电子邮件。但是如果你想专门检查某封邮件,你必须知道数组中实际存储了什么。幸运的是,电子邮件本身已存储,因此您可以遍历数组并检查每封电子邮件。

请参阅ActionMailer::Base以查看可用的配置方法,您可以使用这些方法来确定数组中存在哪些电子邮件。一些最适合您的情况的方法可能是

  • recipients: 接受一个或多个电子邮件地址。这些地址是您的电子邮件将被发送到的地方。设置 To: 标头。
  • subject: 您的电子邮件的主题。设置主题:标题。
于 2010-01-14T12:50:50.993 回答
21

使用当前的 Rspec 语法,我最终使用了以下内容:

last_email = ActionMailer::Base.deliveries.last
expect(last_email.to).to eq ['test@example.com']
expect(last_email.subject).to have_content 'Welcome'

我的测试环境是一个功能规范,我想确保在注册后向用户发送欢迎电子邮件。

于 2013-06-20T20:54:09.423 回答
6

截至 2020 年(Rails 6 时代,可能更早引入),您可以执行以下操作:(使用 SystemTest 示例)TL;DR:使用assert_emailsfromActionMailer::TestHelperActionMailer::Base.deliveries.last访问邮件本身。

require "application_system_test_case"
require 'test_helper'
require 'action_mailer/test_helper'

class ContactTest < ApplicationSystemTestCase
  include ActionMailer::TestHelper

  test "Send mail via contact form on landing page" do
    visit root_url

    fill_in "Message", with: 'message text'

    # Asserting a mail is sent
    assert_emails 1 do
      click_on "Send"
    end

    # Asserting stuff within that mail
    last_email  = ActionMailer::Base.deliveries.last

    assert_equal ['whatever'], last_email.reply_to
    assert_equal "contact", last_email.subject
    assert_match /Mail from someone/, last_email.body.to_s
  end
end

官方文档:

注意 除了在上面的系统测试中手动检查邮件的内容,您还可以测试是否使用了特定的邮件操作,如下所示:

assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"]

And some other handy assertion, see https://api.rubyonrails.org/v6.0.3.2/classes/ActionMailer/TestHelper.html#method-i-assert_emails .

于 2020-09-23T10:28:21.173 回答
3

测试框架应该有一个出色的助手,它可以让您断言有关已发送电子邮件的某些条件。是的,你可以用 ActionMailer.deliveries 自己做,但是应该把它变成一个整洁的小块

于 2010-01-14T16:30:40.817 回答
2

有点晚了,但它可能会帮助其他人:

您可以使用Email-spec,一个 Rspec/Minitest 匹配器和 Cucumber 步骤的集合。

于 2013-08-16T06:41:24.700 回答
1

这是我发现的最好的方法。

1)包括这样的动作邮件回调插件:

script/plugin install git://github.com/AnthonyCaliendo/action_mailer_callbacks.git

我并没有真正使用该插件的主要功能,但它确实提供了能够确定用于发送电子邮件的方法的好功能。

2) 现在你可以像这样在你的 test_helper.rb 中加入一些方法:

  def assert_sent(method_name)
    assert sent_num_times(method_name) > 0
  end

  def assert_not_sent(method_name)
    assert sent_num_times(method_name) == 0
  end

  def assert_sent_once(method_name)
    assert sent_num_times(method_name) == 1
  end

  def sent_num_times(method_name)
    count = 0
    @emails.each do |email|
      count += 1 if method_name == email.instance_variable_get("@method_name")
    end
    count
  end

3) 现在你可以像这样编写甜蜜的测试:

require 'test_helper'
class MailingTest < ActionController::IntegrationTest

  def setup
    @emails = ActionMailer::Base.deliveries
    @emails.clear
  end

  test "should send a mailing" do
    assert_difference "Mailing.count", 1 do
      feeds(:feed1).generate_mailing
    end

    assert_sent_once "broadcast"
    assert_not_sent "failed_mailing"
  end
end

这里的“broadcast”和“mailing_failed”是我的 ActionMailer::Base 类中方法的名称。这些是您通常通过调用Mailer.deliver_broadcast(some_data)等使用的Mailer.deliver_failed_mailing(some_data)。就是这样!

于 2010-05-12T03:23:27.090 回答