Rails - 你如何测试 ActionMailer 在测试中发送了特定的电子邮件
Posted
技术标签:
【中文标题】Rails - 你如何测试 ActionMailer 在测试中发送了特定的电子邮件【英文标题】:Rails - How do you test ActionMailer sent a specific email in tests 【发布时间】:2011-01-05 00:39:02 【问题描述】:目前在我的测试中,我会做这样的事情来测试是否有电子邮件排队等待发送
assert_difference('ActionMailer::Base.deliveries.size', 1) do
get :create_from_spreedly, :user_id => @logged_in_user.id
end
但是,如果我一个控制器操作可以发送两封不同的电子邮件,即如果注册正常则向用户发送一封电子邮件,或者如果出现问题则通知管理员 - 我如何测试实际发送了哪封电子邮件。无论如何,上面的代码都会通过。
【问题讨论】:
【参考方案1】:截至 rails 3 ActionMailer::Base.deliveries 是 Mail::Message 的数组。来自mail documentation:
# 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
【讨论】:
您也可以使用:"assert ActionMailer::Base.deliveries.size >= 1 (or == 2)" 来检查是否发送了多封邮件。并验证多个主题和电子邮件使用“ActionMailer::Base.deliveries.last(2)”【参考方案2】:在测试期间使用 ActionMailer 时,所有邮件都放在一个名为 deliveries
的大数组中。您基本上正在做的(并且大部分情况下就足够了)是检查数组中是否存在电子邮件。
但是如果你想专门检查某封邮件,你必须知道数组中实际存储了什么。幸运的是,电子邮件本身已存储,因此您可以遍历数组并检查每封电子邮件。
请参阅ActionMailer::Base 以查看可用的配置方法,您可以使用这些方法来确定阵列中存在哪些电子邮件。一些最适合您的情况的方法可能是
recipients
:接受一个或多个电子邮件地址。这些地址是您的电子邮件将被发送到的地方。设置 To: 标头。
subject
:你邮件的主题。设置主题:标题。
【讨论】:
【参考方案3】:使用当前的 Rspec 语法,我最终使用了以下内容:
last_email = ActionMailer::Base.deliveries.last
expect(last_email.to).to eq ['test@example.com']
expect(last_email.subject).to have_content 'Welcome'
我的测试上下文是一个功能规范,我想确保在注册后向用户发送欢迎电子邮件。
【讨论】:
【参考方案4】:截至 2020 年(Rails 6 时代,可能更早引入),您可以执行以下操作:
(使用 SystemTest 示例)TL;DR:使用来自 ActionMailer::TestHelper
和 ActionMailer::Base.deliveries.last
的 assert_emails
来访问邮件本身。
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
官方文档:
ActionMailer Guide/Testing Testing Guide/ActionMailer注意 除了像上面的系统测试那样手动检查邮件的内容,您还可以测试是否使用了特定的邮件操作,如下所示:
assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"]
还有一些其他方便的断言,请参阅https://api.rubyonrails.org/v6.0.3.2/classes/ActionMailer/TestHelper.html#method-i-assert_emails。
【讨论】:
【参考方案5】:测试框架shoulda 有一个出色的助手,可以让您断言关于已发送电子邮件的某些条件。是的,你可以用 ActionMailer.deliveries 自己做,但应该把它变成一个整洁的小块
【讨论】:
这是have_sent_email
匹配器rubydoc.info/gems/shoulda/2.11.3/Shoulda/ActionMailer/Matchers(例如it should have_sent_email.with_subject(/spam/).from('do-not-reply@example.com').with_body(/spam/).to('myself@me.com')
【参考方案6】:
有点晚了,但可能对其他人有所帮助:
您可以使用Email-spec,Rspec/Minitest 匹配器和 Cucumber 步骤的集合。
【讨论】:
【参考方案7】:这是我发现的最好的方法。
1) 像这样包含action mailer callbacks 插件:
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)
等使用的。就是这样!
【讨论】:
以上是关于Rails - 你如何测试 ActionMailer 在测试中发送了特定的电子邮件的主要内容,如果未能解决你的问题,请参考以下文章