Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rspec: ActionMailer::Base.deliveries is always empty

I've written an RSpec integration test. According to test.log, I can see that it has sent an email, but when I try to access the email using ActionMailer::Base.deliveries, it always shows it is empty.

I have read the other similar questions and tried everything. I'm stumped.

Here is the code using 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

As you can see above, config.action_mailer.delivery_method = :test

In test.log, it shows that the email really is sent!

Sent mail to [email protected] (62ms)
Date: Tue, 29 Jan 2013 13:53:48 -0500
From: Review Studio <[email protected]>
To: [email protected]
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)
like image 321
Leonard Teo Avatar asked Jan 29 '13 18:01

Leonard Teo


3 Answers

This is an integration test using Capybara and Selenium. Therefore, you have to wait for the application to actually send the mail before checking that it has sent it.

Note - this solves the problem but is generally bad practice

Add a sleep 1 to tell rspec to wait after triggering the send mail event. It then resumes by checking the ActionMailer::Base.deliveries array and passed.

As mentioned, this is generally bad practice because it slows down tests.

Better way

Integration test shouldn't test the mail is sent at all. Tests should be divided up into clear responsibilities for the class being tested. Therefore, we'd structure the tests differently so that we only test for the mail being sent in another class (a controller or resource test). We could also use expectations to check that the call to the mail method was actually made though it's possible that we'd still get timing issues.

like image 153
Leonard Teo Avatar answered Oct 23 '22 22:10

Leonard Teo


You should never use sleep or those time consuming methods on the specs, i't will only slow down your specs!

Try using an expectation on your mailer, at the beginning of your test add something like

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

that way you don't have to wait and you are actually testing what you have to test (that the code creates and delivers the mail) and not the mailer code (you only call deliver on a mail object, the actual delivery is a job of the ActionMailer tests and you have nothing to do with it on your application, you should just trust that calling those method works)

like image 40
arieljuod Avatar answered Oct 24 '22 00:10

arieljuod


In my case config.action_mailer.perform_deliveries was false for test environments.

like image 1
Ali Sepehri.Kh Avatar answered Oct 23 '22 22:10

Ali Sepehri.Kh