Problem
I seem to be facing a stubborn issue with my RSpec tests trying to constantly send emails in test-env despite my configuration should avoid it. Whatever I try it seems to totally ignore it.
My environment
- Rails 6.1.1
- Ruby 3.0.0
- sendgrid-ruby gem 6.3.9
I have a mailer class inheritance-chain as follows: OrganizationMailer
<-ApplicationMailer
<-ActionMailer::Base
In my config/environments/test.rb
I have the following mail-related configuration
config.action_mailer.delivery_method = :test
config.action_mailer.perform_deliveries = false
config.active_job.queue_adapter = :test
My config/application.rb
and config/environment.rb
don't contain any extra configuration.
Bit offtopic maybe, but just in case adding it as well:
I have ensured that the line ENV['RAILS_ENV'] ||= 'test'
is present in both- spec/spec_helper.rb
and spec/rails_helper.rb
since for some reason Rails randomly triggered my tests in staging environment. I also had to change the .rspec
file contents in my project from --require rspec_helper
to --require rails_helper
. I got the solution from here1, here2 and here3. But this I don't think plays any role in current problem.
Bad solution
I hacked my way through this issue atm by just adding the unless Rails.env.test?
on top of each email-sending method I'm having to ensure none of them reach Sendgrid in tests, but this sucks big time I know. That's why I'm posting this question to get this fixed properly without such if/unless-clauses.
My theory
Can it be that sendgrid-ruby is here to blame? I inherit things from ApplicationMailer
and ActionMailer
but eventually what is sending the emails is Sendgrid ruby gem. If so, how to avoid it best? I didn't find any hints from the sendgrid-ruby documentation about that. I will post one of my simple mailer-methods below so you could see the situation atm:
# frozen_string_literal: true
# using SendGrid's Ruby Library
# https://github.com/sendgrid/sendgrid-ruby
require 'sendgrid-ruby'
class MyMailer < ApplicationMailer
include SendGrid
def my_mailer_method(my_object:)
unless Rails.env.test? # <---- Hack I'd like to get rid of
from = Email.new(email: '[email protected]', name: t('general.title'))
to = Email.new(email: my_object.contact_email)
subject = "#{t('my_mailer.my_mailer_method.subject')}: #{my_object.my_object_title}"
content = Content.new(
type: 'text/html',
value: ApplicationController.render(
template: 'my_mailer/my_mailer_method',
locals: {
my_object: my_object
},
layout: nil
)
)
mail = SendGrid::Mail.new(from, subject, to, content)
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
# Send out mail
response = sg.client.mail._('send').post(request_body: mail.to_json)
end
end
end
The issue here is that you may have set
config.action_mailer.delivery_method = :test
but you are not actually using ActionMailer to deliver your emails. Instead, within the MyMailer class you are directly using the SendGrid API, and sidestepping ActionMailer entirely.If you want to use SendGrid API to send your emails, then I actually recommend using the sendgrid-actionmailer gem. It allows you to use ActionMailer to build your emails and uses the SendGrid API under the hood to send them. This allows you to send other parameters that the API supports and would be more difficult or impossible with SMTP, while still using the Rails standard ActionMailer to send the emails.
To ensure that your mails are sent by SendGrid in production, but not sent in test, you would set:
in your
production.rb
environment. And set:as you already have in your
test.rb
environment.