0

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: 'no-reply@my.domain', 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
Andres
  • 2,099
  • 3
  • 22
  • 39
  • Have you considered stubbing the method (**my_mailer_method**) in your specs? – flacko Apr 06 '22 at 13:19
  • The ActionMailer settings are going to be completely irrelevant here as your not actually using it to send emails. Its unclear if your actually using any part of ActionMailer at all in fact. If you want to clean this up I would suggest you separate out the boilerplate code of actually sending the message from composing the message. – max Apr 06 '22 at 15:43
  • If you don't need the fancy API methods I would consider just using STMP and using ActionMailer as usual. https://docs.sendgrid.com/for-developers/sending-email/rubyonrails – max Apr 06 '22 at 15:46
  • @flacko I guess that would be one way indeed, but how come the `config/environments/test.rb` mailer-configuration does not affect anything? It's more of a question to sendgrid-ruby gem I guess as this is used to send the email. But I'm not sure if sendgrid-gem and actionmailer are totally separated logic. – Andres Apr 06 '22 at 18:01
  • @max yes the email sending seems to go through sendgrid-ruby gem method as explained in the question but can I be sure the sendgrid-ruby gem does not work together with actionmailer in any way? I guess the email-sending code separation would sound good in any case, thanks! – Andres Apr 06 '22 at 18:03

1 Answers1

2

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.

philnash
  • 70,667
  • 10
  • 60
  • 88