ActionMailer在测试环境中未能发送确认电子邮件 - Rails 4

3
我正在使用Rails(4.2.6),Ruby(2.2.4),Devise(4.1.1),Capybara(2.7.1),Capybara-email(2.5.0),Email_spec(2.1.0),Rspec(3.4.0)和Postgres(0.18.4)。
在将应用程序从Rails 4.1.15升级到4.2.6后,多个身份验证和注册测试失败了。 在升级之前,所有测试都能够通过。 代码在开发环境中按预期工作(例如,确认电子邮件已发送并且可以在Rails服务器终端中查看)。 仅在测试环境中存在未传递的电子邮件问题。
以下是失败的rspec ./spec/features/users/authentification_spec.rb:56:
#Sign up User

    visit "/"
    click_link "Sign up"

    find(:css, "#user_email").set("tester9@example.com")
    find(:css, "#user_password").set("password900")
    find(:css, "#user_password_confirmation").set("password900")

    expect {
            click_button "Sign up"      
        }.to change{ ActionMailer::Base.deliveries.size}.by(1)

当用户填写表单并点击“注册”按钮时,页面将重定向到“关于”页面,预期会出现以下闪存消息:“已向您的电子邮件地址发送包含确认链接的消息,请按照链接激活您的帐户。”
使用save_and_open_page,我确认了上述行为。然而,该规范失败,并显示以下错误:
Failure/Error:
   expect {
           click_button "Sign up"  
       }.to change{ ActionMailer::Base.deliveries.size}.by(1)

   expected result to have changed by 1, but was changed by 0
 # ./spec/features/users/authentification_spec.rb:56:in `block (2 levels) in <top (required)>'

错误表明ActionMailer::Base.deliveries数组中没有消息对象。Pry的结果证实了ActionMailer::Base.deliveries数组确实为空:
[1] pry(main)> mail = ActionMailer::Base.deliveries
=> []

以下是运行规格时的测试日志:
Started POST "/users" for 127.0.0.1 at 2016-06-09 16:16:25 -0700
Processing by RegistrationsController#create as HTML
  Parameters: {"utf8"=>"✓", "user"=>{"email"=>"tester9@example.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
  [1m[35m (0.8ms)[0m  SAVEPOINT active_record_1
  [1m[36mUser Exists (3.3ms)[0m  [1mSELECT  1 AS one FROM "users" WHERE "users"."email" = 'tester9@example.com' LIMIT 1[0m
  [1m[35mUser Exists (1.9ms)[0m  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('tester9@example.com') LIMIT 1
  [1m[36mSQL (2.1ms)[0m  [1mINSERT INTO "users" ("email", "encrypted_password", "signup_as", "created_at", "updated_at", "confirmation_token",     "confirmation_sent_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"[0m  [["email", "tester9@example.com"], ["encrypted_password", "$2a    $04$Vruf8j0A.DdOZLPe0qjVp.4PxzdR7sCdLF4FfyAr4dQSxcQAjpAwy"], ["created_at", "2016-06-09 23:16:25.095386"], ["updated_at", "2016-06-09 23:16:25.095386"],     ["confirmation_token", "EmY8JyaVAxAfiq7oQ98z"], ["confirmation_sent_at", "2016-06-09 23:16:25.097581"]]
  [1m[35m (0.4ms)[0m  RELEASE SAVEPOINT active_record_1
Redirected to http://www.example.com/about
Completed 302 Found in 113ms (ActiveRecord: 9.6ms)

日志显示确认邮件已发送,但交付数组为空。为什么会这样?是否由于某些原因未将记录提交或持久化到测试数据库中?我已阅读有关邮件未发送的相关帖子,但没有找到解决我的问题的方法。

来自test.rb的相关代码:

  # Tell Action Mailer not to deliver emails to the real world.
  # The :test delivery method accumulates sent emails in the
  # ActionMailer::Base.deliveries array.  

  config.action_mailer.delivery_method = :test
  config.action_mailer.perform_deliveries = true

来自rails_helper.rb的相关内容:

require 'rspec/rails'
require 'devise'
require 'capybara/rails'
require 'database_cleaner'
require 'capybara/poltergeist'
require 'capybara/email/rspec'
require 'email_spec'

  # Includes Devise test helpers (e.g., lets you use Devise's "sign_in" method in tests)

  config.include Devise::TestHelpers, :type => :controller 
  config.include Warden::Test::Helpers

devise.rb 中相关的注释代码:

  # Configure the class responsible to send e-mails.
   #config.mailer = 'Devise::Mailer'

邮件在开发和生产环境中都能正常发送。但是在测试环境中出现了问题,我该如何解决呢?请帮忙!谢谢!


日志在哪里显示邮件已发送?它显示用户已保存,但没有显示任何关于电子邮件的内容。你是否使用#deliver_later发送电子邮件(这是4.2的一个重要功能之一)? - Thomas Walpole
谢谢,你说得对,测试日志中没有任何迹象表明电子邮件实际上已发送。我只在开发日志中看到了电子邮件活动。不,我没有使用#deliver_later。 - codeinspired
好的 - 这是JS测试吗?如果是,您可能需要在单击按钮后睡一会儿,以便给动作足够的时间发生,然后再检查deliveries.size - 另外,您能否添加实际发送邮件的代码? - Thomas Walpole
不,这不是JS测试。然而,我尝试了“休眠”测试,但并没有解决问题。我正在使用Devise的confirmable模块。ActiveMail一直处理发送邮件。在升级测试模式之前,它运行得非常好。 - codeinspired
2个回答

6

1
我会尝试,但是我如何“禁用该测试的事务性测试?” 我该如何设置? - codeinspired
2
假设您正在使用database_cleaner来控制测试的数据库策略(例如js测试的截断等),您可以在database cleaner配置中添加一个before块,如下所示 - config.before(:each, truncation: true) do Database::Cleaner.strategy = :truncation end,然后将测试标记为truncation: true。您也可以只标记测试为js: true-但是之后您肯定需要在按钮点击后进行睡眠。如果您没有使用database_cleaner-那么您如何控制不同类型测试的数据库策略呢? - Thomas Walpole
1
是的,我正在使用Database Cleaner。尝试将测试设置为truncation:true,但规范仍然失败并显示相同的错误。尝试将测试设置为js:true和sleep(0.02),但出现错误:“Failure/Error: find(:css,“#user_password”).set(“password900”)” - codeinspired
1
睡眠时间需要比js: true更长 - 尝试使用sleep 2进行测试 - 由于它似乎有效,这意味着您的before块可能没有被调用 - 我无法查看您的代码以告诉您原因。 - Thomas Walpole
1
非常感谢!这些更改修复了问题:rails_helper.rb: config.before(:each, truncation: true) do DatabaseCleaner.strategy = :truncation end 然后在测试中调用“truncate true”。现在按预期工作! - codeinspired
@ThomasWalpole 感谢您的回复。我遇到了这个问题已经有几天了。我在 rails_helper 中添加了这个代码块 config.before(:each, truncation: true) do DatabaseCleaner.strategy = :truncation end,并在示例块中添加了 :truncation :true 标志,现在测试通过了。 - bir_ham

3

为了总结Tom Walpole和codeinspired之间的讨论,针对那些可能不会查看评论的浏览器,他们的解决方案是(假设您使用DatabaseCleaner)进行以下修改:

rails_helper.rb

# lots of boilerplate and
# other things at the top of this file, but you'll
# eventually see....

RSpec.configure do |config|

  # ... other config stuff ...

  # sometime after this key line, you want to add:
  config.before(:each, truncation: true) do
    DatabaseCleaner.strategy = :truncation
  end

  # ... other config stuff ...

end

对于此处所讨论的测试,您希望将其修改为如下所示(请注意在以单词 "it" 开始的行中添加了 "truncation: true")

something_spec.rb

# ... other stuff ...
describe "some function" do
  it "performs a function correctly", truncation: true do
    # your test
    # goes here
  end
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接