I'm using the Clearance gem in Rails along with Capybara and Minitest and I can't figure out how to test the password reset mailer. I'm not trying to test the Clearance gem, which is already well-tested, but I'd like a high-level integration test to ensure the expected user experience doesn't break, and that includes the mailer.
Here's the test that I can't complete (/integration/password_reset_test.rb):
require 'test_helper'
class PasswordResetTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
@user = create(:user)
end
test "User can reset their password" do
visit "/login"
assert page.current_path == "/login"
click_link "Forgot password?"
assert page.current_path == "/passwords/new"
assert_selector "h1", text: "Reset your password"
fill_in("Email", :with => @user.email)
click_button "Reset your password"
assert page.current_path == "/passwords"
assert_selector "h1", text: "Your password reset email is on the way."
# This is where I'm stuck
# Would like to test that the correct email was sent by checking
# basic content in the email and then simulate a user clicking
# the password reset link and then updating their password.
end
end
How do you actually test that the mail was sent properly and is there a way to simulate capybara clicking through the password reset link in the email and then filling out the form to reset the password?
I tried this as well, but this line failed, so I'm clearly not doing something right:
assert_equal 1, ActionMailer::Base.deliveries.size
I can test manually by grabbing the password reset email link from the server logs, so the feature is working properly.
All of the examples I can find online are assuming you're using Rspec, but nothing for Minitest. I also tried using the capybara-email gem, but that did not have Minitest examples and I couldn't get that working either.
For reference: Gemfile test_helper.rb
For what you want to do capybara-email
is a good choice. To set it up you would include it either into ActionDispatch::IntegrationTest
or into the individual test class where it's needed (in your current case - PasswordResetTest
). You, most likely, also need to configure ActiveJob to perform jobs when they are queued rather than delaying them (otherwise the emails won't actually be sent). One way to do that is by including ActiveJob::TestHelper
and then using the perform_enqueued_jobs
method it provided. That leads to something along the lines of
require 'test_helper'
class PasswordResetTest < ActionDispatch::IntegrationTest
include Capybara::Email::DSL
include ActiveJob::TestHelper
def setup
clear_emails
@user = create(:user)
end
test "User can reset their password" do
perform_enqueued_jobs do
visit "/login"
assert_current_path("/login")
click_link "Forgot password?"
assert_current_path("/passwords/new")
assert_selector "h1", text: "Reset your password"
fill_in("Email", :with => @user.email)
click_button "Reset your password"
assert_current_path("/passwords")
assert_selector "h1", text: "Your password reset email is on the way."
end
open_email(@user.email)
assert_content(current_email, 'blah blah')
current_email.click_link('Reset Password')
assert_current_path(reset_password_path)
... # fill out form with new password, etc.
end
end
Note the use of assert_current_path
rather than assert page.current_path...
- You generally want to prefer the former since the latter has no waiting/retrying behavior and can lead to flaky tests. Also note that writing tests that hardcode in the path names leads to a nightmare if you ever want to change the path names so you might be better off writing your codes using the rails provided routes helpers instead
assert_current_path(login_path)
etc.