13

After upgrading to Rails 6 I am noticing that default mailer's .deliver_later is not working the same as in Rails 5.

Configuration:

config.active_job.queue_adapter = :inline

When running Mailer.register_email(...).deliver_later - nothing is stored in ActionMailer::Base.deliveries. This array gets filled if I run perform_enqueued_jobs - it seams like queue_adapter = :inline doesn't work the way I expect it to work.

If I run Mailer.send(...).deliver_now then ActionMailer::Base.deliveries has proper value in it.

Any idea why this is happening and how to solve this?

knagode
  • 5,816
  • 5
  • 49
  • 65
  • 2
    This behavior changed somewhere between Rails 5 and Rails 6. I couldn't find where, but I did see that Rails is now calling `perform_enqueued_jobs` in their Minitest email helpers. They add a filter to ensure only email jobs are performed this way. https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/test_helper.rb#L37 – DylanReile Sep 04 '19 at 23:10

3 Answers3

6

I had same problem in my tests. A search on the Internet yielded nothing, so I started experimenting.

I tried wrapping the call method of sending mail in

assert_emails 1 do
  Mailer.register_email(...).deliver_later
end

After that, ActionMailer::Base.deliveries populated correctly.

RomanOks
  • 692
  • 3
  • 13
2

The issue

The issue lies in two new lines of code added to Rails 6 (line 1 & line 2), where basically, the callback before_setup defined here (in RSpec) and here (in Minitest) gets overridden (by this), thus forcing the queue_adapter to be the test adapter instead of the one defined by config.active_job.queue_adapter.

Workaround

So in order to use the queue_adapter defined by config.active_job.queue_adapter and therefore restore the Rails 5 behaviour we can do something like the below.

# spec/support/active_job/test_helper.rb
module ActiveJob
  module TestHelper
    def before_setup
      super
    end
  end
end
Andres Leon
  • 131
  • 1
  • 8
  • Thank you, @andres-leon! You saved me quite some time to debug this issue for me! Great answer! – DjezzzL May 20 '23 at 07:12
0

If the exact number of emails could easily change, this is another option:

assert_changes 'enqueued_jobs.size' do
  # Some code that sends email with deliver_later
end

This allows you to test that emails were sent but it disregards the exact number (which is a limitation of the asserts_emails method - other than this, the asserts_emails method is great). I found that the enqueued_jobs method is very helpful in testing anything background jobs, including deliver_later

NOTE: the above example only checks that the enqueued jobs list was changed. If you want to be more specific and check that the queue was changed with emails, you should do this:

assert_changes 'enqueued_jobs.select {|job| job["job_class"] == "ActionMailer::MailDeliveryJob"}.size' do
  # Some code that sends email with deliver_later
end

Clark Taylor
  • 323
  • 2
  • 10