Rspec:ActionMailer :: Base.deliveries总是为空

时间:2013-01-29 18:59:59

标签: ruby-on-rails rspec

我写了一个RSpec集成测试。根据test.log,我可以看到它已经发送了一封电子邮件,但是当我尝试使用ActionMailer :: Base.deliveries访问该电子邮件时,它总是显示它是空的。

我已经阅读了其他类似的问题,并尝试了一切。我很难过。

以下是使用Capybara / RSpec的代码:

it "should notify owner" do

  puts "Delivery method: #{ActionMailer::Base.delivery_method.inspect}"
  # This returns: ":test", which is correct

  # Get other guests to do the review
  @review = Review.last
  share_link = "http://#{@account.subdomain}.cozimo.local:#{Capybara.server_port}/review/#{@review.slug}"

  visit(share_link)

  fill_in "email", :with => @user1.email
  fill_in "password", :with => "foobar"

  click_button "Start Review"

  # Add a comment
  click_on "ice-global-note-button"
  find(:css, "#ice-global-note-panel > textarea.ui-corner-all").set "Foo"
  click_on "ice-global-note-submit-button"

  # Test is failing here. WTF? The array should not be empty
  ActionMailer::Base.deliveries.empty?.should be_false

  # Check that a notification email was sent to the owner
  open_email(@owner.email)
  current_email.should have_content "Hi #{@owner.first_name}"

end

如上所示,config.action_mailer.delivery_method =:test

在test.log中,它显示电子邮件确实已发送!

Sent mail to somebody1@example.com (62ms)
Date: Tue, 29 Jan 2013 13:53:48 -0500
From: Review Studio <support@cozimo.com>
To: somebody1@example.com
Message-ID: <51081abcd9fbf_5bd23fef87b264a08066@Leonards-MacBook-Pro.local.mail>
Subject: Notes added for Test
Mime-Version: 1.0
Content-Type: text/plain;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

Hi Bruce,

Just letting you know that you have new notes added to the review:

  Project: Test
  Description: Test description
  URL: http://ballistiq.cozimo.local:3000/review/625682740

Thanks,

Review Studio
Completed 200 OK in 170ms (Views: 0.2ms | ActiveRecord: 4.3ms)

6 个答案:

答案 0 :(得分:8)

这是使用Capybara和Selenium的集成测试。因此,您必须等待才能让应用程序在检查已发送邮件之前实际发送邮件。

注意 - 这解决了问题,但通常是不好的做法

添加sleep 1告诉rspec在触发发送邮件事件后等待。然后通过检查ActionMailer :: Base.deliveries数组并传递它来恢复。

如上所述,这通常是不好的做法,因为它会减慢测试速度。

更好的方式

集成测试不应该测试邮件是否完全发送。测试应分为明确的测试课程职责。因此,我们将以不同的方式构建测试,以便我们仅测试在另一个类(控制器或资源测试)中发送的邮件。我们也可以使用期望来检查是否实际调用了mail方法,尽管我们仍然可能会遇到时间问题。

答案 1 :(得分:4)

你不应该在规格上使用睡眠或那些耗时的方法,我只会放慢你的规格!

尝试在邮件中使用期望,在测试开始时添加类似

的内容
mail = mock(mail)
mail.should_receive(:deliver)
YourMailer.should_receive(:your_method).once.and_return(mail)

通过这种方式,您无需等待,并且您实际上正在测试您必须测试的内容(代码创建并传递邮件)而不是邮件代码(您只调用邮件对象上的传递,实际传递是ActionMailer测试的一个工作,你在你的应用程序上没有任何关系,你应该相信调用这些方法是有效的)

答案 2 :(得分:1)

对于我来说,config.action_mailer.perform_deliveries对于测试环境是false

答案 3 :(得分:0)

你也可以使用这个wait_for_ajax助手代替睡眠进行javascript POST。这将使Capybara等待ajax完成。

module WaitForAjax
  def wait_for_ajax
   Timeout.timeout(Capybara.default_wait_time) do
    loop until finished_all_ajax_requests?
   end
  end

  def finished_all_ajax_requests?
   page.evaluate_script('jQuery.active').zero?
  end
end

RSpec.configure do |config|
  config.include WaitForAjax, type: :feature
end

来源: https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara

答案 4 :(得分:0)

我认为通常你想在测试中避免使用sleep()。如果您正在与Capybara进行集成测试,则只需使用method that waits.

例如,此测试间歇性失败(valid_step_7发送电子邮件):

it "should get past step 7 and send an email" do
  valid_step_5_no_children
  valid_step_7

  expect(ActionMailer::Base.deliveries.count).to eq(1)
  expect(page).to have_content "I AM JOHN DOE"
end

这是因为它正在完成最后一步(valid_step_7)并立即检查'ActionMailer :: Base.deliveries.count'以查看它是否等于1,但它不一定没有时间来填充交付数组(从我的调试开始)。

我没有随机失败,因为我将测试转到首先检查下一页的内容,给出ActionMailer::Base.deliveries的时间来填充并执行检查:

it "should get past step 7 and send an email" do
  valid_step_5_no_children
  valid_step_7

  expect(page).to have_content "I AM JOHN DOE"
  expect(ActionMailer::Base.deliveries.count).to eq(1)
end

我怀疑sleep(1)会起作用,但我认为你在不需要的时候会强迫睡眠。

答案 5 :(得分:0)

我遇到了同样的问题,最后我找到了一个简单的解决方案,不确定是否是最好的扫描仪:欢迎所有的赞赏或更正。

首先在environtments / test.rb中添加此行 config.active_job.queue_adapter = :inline,即使我们输入代码deliver_later

,也会执行“deliver_now”

在测试中添加对电子邮件的期望(我总是显示一条消息,告知已发送电子邮件...),例如:

expect(page).to have_content("We have sent a confirmation e-mail")
expect(ActionMailer::Base.deliveries.count).to eq(1)

此第一个期望将等待直到2秒(是默认超时,尽管可通过Capybara.default_wait_time = 2s配置)。

所以,在我的情况下,把这个期望放在config/environments/test.rb中添加这个配置行就足以正确地传递测试了