Rspec: ActionMailer :: Base.deliveries is always empty

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

I read other similar questions and tried everything. I'm at a dead end.

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 is indeed sent!

 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.ma il> 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) 
+4
source share
5 answers

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

Note. This solves the problem, but this is usually bad practice.

Add sleep 1 to tell rspec to wait after the mail send event is fired. Then it resumes by checking the ActionMailer :: Base.deliveries array and passing it.

As already mentioned, this is usually a bad practice as it slows down the tests.

The best way

The integration test should not verify that mail is being sent at all. Tests should be divided into clear responsibilities for the class being tested. Therefore, we will structure the tests in different ways, so that we only check for sending mail in another class (controller or resource test). We could also use expectations to verify that the mail method call was actually made, although it is possible that we will still have timing problems.

+8
source

You should never use sleep or those laborious methods in specifications, I only slow down your specifications!

Try using wait in your email program, 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) 

this way you donโ€™t have to wait, and you actually check what you need to check (that the code creates and delivers the mail) and not the code of the mailer (you only call delivery on the mail object, the actual delivery is the work of the ActionMailer tests, and you you have nothing to do with your application, you just have to trust that calling these methods works)

+4
source

You can also use this wait_for_ajax helper instead of sleeping for your javascript POST. This will make Capybara wait for ajax to complete.

 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 

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

0
source

I think, in general, you want to avoid sleep() in your tests. If you are doing an integration test with Capybara, you just need to use the method that is waiting.

For example, this test is interrupted intermittently ( valid_step_7 sends an email):

 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 

This is because it finishes the last step (valid_step_7) and instantly checks "ActionMailer :: Base.deliveries.count" to see if it is equal to 1, but it didnโ€™t have time to populate the array supplies (from my debug anyway) .

I did not have any random crashes since I turned my test over to check the contents on the next page first, give time to populate ActionMailer::Base.deliveries and do the check:

 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 

I suspect that sleep(1) will work, but I think that you force 1 second to sleep when it is not needed.

0
source

I had the same problem, and finally I found a simple solution, not sure if the best wat: all ratings or corrections are welcome.

First of all, adding this line to environmenttments / test.rb config.active_job.queue_adapter = :inline , this will do delivery_now, even if we add our deliver_later code

In the test, add a wait for the email message (I always show a message that the message was sent ...), for example:

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

this first wait will wait up to 2 seconds (this is the default timeout, while it is configured via Capybara.default_wait_time = ss ).

So, in my case, setting this expectation and adding this configuration line to test.rb is enough for the test to pass correctly

0
source

All Articles