on
Testing Email with Rspec 3
It was a strange day for me but I wrote a post on August 2, 2011 on how to test email with RSpec.
Nearly 3 years later, I can look back and say it’s not so bad but it no longer works. There are many things deprecated and permanent removed with RSpec 3.0.
Now that I have better sense of structure, I would have never written integration tests that way. The post Testing Some Devise Features With RSpec, Steak and Email Spec is a reminder of why I write often.
On an existing Rails application, you can simply add this on your Gemfile.
group :test do
gem 'email_spec'
gem 'factory_girl_rails'
gem 'rspec-rails', '~> 3.0.0'
# other gems
end
Install the gems and read about RSpec features if you are not familiar with it. There is no need for the Steak gem. I think it has no advantages anymore and may simply exist because people want to mock or dislike Cucumber.
A typical feature for testing sign up (Devise) may look like this:
require 'rails_helper'
feature "Sign up" do
let(:user) { build(:user) }
scenario "User is not registered" do
visit sign_up_path
reset_mailer
fill_in 'user_email', with: user.email
fill_in 'user_password', with: user.password
fill_in 'user_password_confirmation', with: user.password
click_button 'Sign up'
expect(page).to have_text("A message with a confirmation link has been sent to your email address.
Please open the link to activate your account")
expect(unread_emails_for(user.email).count).to eq(1)
open_email(user.email)
expect(current_email).to have_body_text("You can confirm your account email through the link below")
click_first_link_in_email
expect(page).to have_content('Sign in')
fill_in 'user_email', with: user.email
fill_in 'user_password', with: user.password
click_button 'Sign in'
expect(page).to have_text("Sign out")
end
end
It works. Notice that on the old post, I used the should syntax which will no longer work. The new syntax is easier to understand for beginners. This is the primary reason for the change.
The example above works but it is not DRY. You need to create a helper.
# spec/support/features/session_helpers.rb
module Features
module SessionHelpers
def sign_up(email, password)
visit sign_up_path
fill_in 'user_email', with: email
fill_in 'user_password', with: password
fill_in 'user_password_confirmation', with: password
click_button 'Sign up'
end
def sign_in(user)
visit sign_in_path
fill_in 'user_email', with: user.email
fill_in 'user_password', with: user.password
click_button 'Sign in'
end
end
end
Update the RSpec config to include this helper.
RSpec.configure do |config|
# Other config options
config.include(EmailSpec::Helpers)
config.include(EmailSpec::Matchers)
config.include Features::SessionHelpers, type: :feature
end
Now it is possible to reuse this for other features.
require 'rails_helper'
feature "Sign up" do
let(:user) { build(:user) }
scenario 'User is not registered' do
reset_mailer
sign_up(user.email, user.password)
expect(page).to have_text('A message with a confirmation link has been sent to your email address.
Please open the link to activate your account')
expect(unread_emails_for(user.email).count).to eq(1)
open_email(user.email)
expect(current_email).to have_body_text('You can confirm your account email through the link below')
click_email_link_matching(/#{user_confirmation_path}/)
expect(page).to have_content('Sign in')
sign_in(user)
expect(page).to have_content('Sign out')
end
end
Using the helper click_email_link_matching
is much better because most of the time, we need to make sure it’s correct.