How to test Rails mailers using RSpec
ActionMailer module has been reconstructed in Rails 3 and mailers have their own subdirectory (app/mailers) since then.
This blog post will demostrate how to test them in Rails using RSpec. Assuming that we have a mailer like the following:
rubyclass Notifier < ActionMailer::Base default from: 'noreply@company.com' def instructions(user) @name = user.name @confirmation_url = confirmation_url(user) mail to: user.email, subject: 'Instructions' end end
To send an email through a method from User
model:
rubyclass User def send_instructions Notifier.instructions(self).deliver_now end end
Before testing it, make sure the config/environments/test.rb file has the following configuration:
rubyRails.application.configure do config.action_mailer.delivery_method = :test end
It ensures that emails won't be sent, but instead be stored on ActionMailer::Base.deliveries array.
So, in order to create the tests:
ruby# spec/models/user_spec.rb RSpec.describe User, type: :model do subject { create :user } it 'sends an email' do expect { subject.send_instructions } .to change { ActionMailer::Base.deliveries.count }.by(1) end end
ruby# spec/mailers/notifier_spec.rb RSpec.describe Notifier, type: :mailer do describe 'instructions' do let(:user) { mock_model User, name: 'Lucas', email: 'lucas@email.com' } let(:mail) { described_class.instructions(user).deliver_now } it 'renders the subject' do expect(mail.subject).to eq('Instructions') end it 'renders the receiver email' do expect(mail.to).to eq([user.email]) end it 'renders the sender email' do expect(mail.from).to eq(['noreply@company.com']) end it 'assigns @name' do expect(mail.body.encoded).to match(user.name) end it 'assigns @confirmation_url' do expect(mail.body.encoded) .to match("http://aplication_url/#{user.id}/confirmation") end end end
Rails allows you to create very comprehensive tests for mailers.
Happy coding!