How to avoid sending emails in Rails test-env with sendgrid-ruby?

932 Views Asked by At

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

  1. Rails 6.1.1
  2. Ruby 3.0.0
  3. 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
1

There are 1 best solutions below

0
On

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:

config.action_mailer.delivery_method = :sendgrid_actionmailer

in your production.rb environment. And set:

config.action_mailer.delivery_method = :test
config.action_mailer.perform_deliveries = false

as you already have in your test.rb environment.