I will add my answer because none of the others was good enough for me:
1) There is no need to mock the Mailer: Rails basically does that already for you.
2) There is no need to really trigger the creation of the email: this will consume time and slow down your test!
That's why in environments/test.rb
you should have the following options set:
config.action_mailer.delivery_method = :test
config.active_job.queue_adapter = :test
Again: don't deliver your emails using deliver_now
but always use deliver_later
. That prevents your users from waiting for the effective delivering of the email. If you don't have sidekiq
, sucker_punch
, or any other in production, simply use config.active_job.queue_adapter = :async
. And either async
or inline
for development environment.
Given the following configuration for the testing environment, you emails will always be enqueued and never executed for delivery: this prevents your from mocking them and you can check that they are enqueued correctly.
In you tests, always split the test in two:
1) One unit test to check that the email is enqueued correctly and with the correct parameters
2) One unit test for the mail to check that the subject, sender, receiver and content are correct.
Given the following scenario:
class User
after_update :send_email
def send_email
ReportMailer.update_mail(id).deliver_later
end
end
Write a test to check the email is enqueued correctly:
include ActiveJob::TestHelper
expect { user.update(name: 'Hello') }.to have_enqueued_job(ActionMailer::DeliveryJob).with('ReportMailer', 'update_mail', 'deliver_now', user.id)
and write a separate test for your email
Rspec.describe ReportMailer do
describe '#update_email' do
subject(:mailer) { described_class.update_email(user.id) }
it { expect(mailer.subject).to eq 'whatever' }
...
end
end
- You have tested exactly that your email has been enqueued and not a generic job.
- Your test is fast
- You needed no mocking
When you write a system test, feel free to decide if you want to really deliver emails there, since speed doesn't matter that much anymore. I personally like to configure the following:
RSpec.configure do |config|
config.around(:each, :mailer) do |example|
perform_enqueued_jobs do
example.run
end
end
end
and assign the :mailer
attribute to the tests were I want to actually send emails.
For more about how to correctly configure your email in Rails read this article: https://medium.com/@coorasse/the-correct-emails-configuration-in-rails-c1d8418c0bfd