I'm using 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), and Postgres (0.18.4)
After I upgraded the application from Rails 4.1.15 to 4.2.6, several authentification & registration tests fail. Before the upgrade all tests were properly passing. The code works as expected in the development environment (for example, confirmation emails are sent & viewable in the Rails server terminal). The problem of non-delivered emails only occurs in the test environment.
Here's the failing 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)
When a user completes the form and clicks the "Sign up" button, the page redirects to the "About" page and as expected, the following flash message appears: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
Using save_and_open_page, I confirmed the aforementioned behavior. However, the spec fails with the following error:
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)>'
The error indicates that there are no message objects in the ActionMailer::Base.deliveries array. The results of Pry confirm that the ActionMailer::Base.deliveries array is indeed empty:
[1] pry(main)> mail = ActionMailer::Base.deliveries
=> []
Here's the test log when the spec is run:
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)
The log indicates that a confirmation email was sent, but the deliveries array is empty. Why is that happening? Is the record not being committed or persisted in the test database for some reason? I've read related posts about non-delivery of mail, but have not found a solution to my problem.
Relevant code from 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
Relevant contents from 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
Relevant commented-out code from devise.rb:
# Configure the class responsible to send e-mails.
#config.mailer = 'Devise::Mailer'
Mail delivery works as expected in development & production. What is going wrong in the test environment? How can I fix it? Thanks!
Looking at devise - https://github.com/plataformatec/devise/blob/4-1-stable/lib/devise/models/confirmable.rb#L48 - it sends the confirmation email in an after_commit callback - If you're running with transactional testing enabled the after_commit callback will never be called (because the DB transaction is rolled back and never committed) so the email is never sent. Disable transaction based testing for that test and it will probably work.
Just to summarize the discussion between Tom Walpole and codeinspired above into a top-level answer for browsers who might not look into comments, their solution (assuming you use DatabaseCleaner) is to make the following modifications:
rails_helper.rb
For the test that is in question here, you want to modify it to look like the following (note the addition of "truncation: true" in the line starting with the word "it")
something_spec.rb