Module: ActionMailer::TestHelper

Includes:
ActiveJob::TestHelper
Included in:
ActionMailer::TestCase::Behavior
Defined in:
actionmailer/lib/action_mailer/test_helper.rb

Overview

Provides helper methods for testing Action Mailer, including #assert_emails and #assert_no_emails.

Constant Summary

Constants included from ActiveSupport::Testing::Assertions

ActiveSupport::Testing::Assertions::UNTRACKED

Instance Method Summary collapse

Methods included from ActiveJob::TestHelper

#after_teardown, #assert_enqueued_jobs, #assert_enqueued_with, #assert_no_enqueued_jobs, #assert_no_performed_jobs, #assert_performed_jobs, #assert_performed_with, #before_setup, #perform_enqueued_jobs, #queue_adapter, #queue_adapter_for_test

Methods included from ActiveSupport::Testing::Assertions

#assert_changes, #assert_difference, #assert_no_changes, #assert_no_difference, #assert_not, #assert_nothing_raised, #assert_raises

Instance Method Details

#assert_emails(number, &block) ⇒ Object

Asserts that the number of emails sent matches the given number.

def test_emails
  assert_emails 0
  ContactMailer.welcome.deliver_now
  assert_emails 1
  ContactMailer.welcome.deliver_now
  assert_emails 2
end

If a block is passed, that block should cause the specified number of emails to be sent.

def test_emails_again
  assert_emails 1 do
    ContactMailer.welcome.deliver_now
  end

  assert_emails 2 do
    ContactMailer.welcome.deliver_now
    ContactMailer.welcome.deliver_later
  end
end


35
36
37
38
39
40
41
42
# File 'actionmailer/lib/action_mailer/test_helper.rb', line 35

def assert_emails(number, &block)
  if block_given?
    diff = capture_emails(&block).length
    assert_equal number, diff, "#{number} emails expected, but #{diff} were sent"
  else
    assert_equal number, ActionMailer::Base.deliveries.size
  end
end

#assert_enqueued_email_with(mailer, method, params: nil, args: nil, queue: nil, &block) ⇒ Object

Asserts that a specific email has been enqueued, optionally matching arguments and/or params.

def test_email
  ContactMailer.welcome.deliver_later
  assert_enqueued_email_with ContactMailer, :welcome
end

def test_email_with_parameters
  ContactMailer.with(greeting: "Hello").welcome.deliver_later
  assert_enqueued_email_with ContactMailer, :welcome, args: { greeting: "Hello" }
end

def test_email_with_arguments
  ContactMailer.welcome("Hello", "Goodbye").deliver_later
  assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"]
end

def test_email_with_named_arguments
  ContactMailer.welcome(greeting: "Hello", farewell: "Goodbye").deliver_later
  assert_enqueued_email_with ContactMailer, :welcome, args: [{ greeting: "Hello", farewell: "Goodbye" }]
end

def test_email_with_parameters_and_arguments
  ContactMailer.with(greeting: "Hello").welcome("Cheers", "Goodbye").deliver_later
  assert_enqueued_email_with ContactMailer, :welcome, params: { greeting: "Hello" }, args: ["Cheers", "Goodbye"]
end

def test_email_with_parameters_and_named_arguments
  ContactMailer.with(greeting: "Hello").welcome(farewell: "Goodbye").deliver_later
  assert_enqueued_email_with ContactMailer, :welcome, params: { greeting: "Hello" }, args: [{farewell: "Goodbye"}]
end

def test_email_with_parameterized_mailer
  ContactMailer.with(greeting: "Hello").welcome.deliver_later
  assert_enqueued_email_with ContactMailer.with(greeting: "Hello"), :welcome
end

def test_email_with_matchers
  ContactMailer.with(greeting: "Hello").welcome("Cheers", "Goodbye").deliver_later
  assert_enqueued_email_with ContactMailer, :welcome,
    params: ->(params) { /hello/i.match?(params[:greeting]) },
    args: ->(args) { /cheers/i.match?(args[0]) }
end

If a block is passed, that block should cause the specified email to be enqueued.

def test_email_in_block
  assert_enqueued_email_with ContactMailer, :welcome do
    ContactMailer.welcome.deliver_later
  end
end

If args is provided as a Hash, a parameterized email is matched.

def test_parameterized_email
  assert_enqueued_email_with ContactMailer, :welcome,
    args: {email: '[email protected]'} do
    ContactMailer.with(email: '[email protected]').welcome.deliver_later
  end
end


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'actionmailer/lib/action_mailer/test_helper.rb', line 157

def assert_enqueued_email_with(mailer, method, params: nil, args: nil, queue: nil, &block)
  if mailer.is_a? ActionMailer::Parameterized::Mailer
    params = mailer.instance_variable_get(:@params)
    mailer = mailer.instance_variable_get(:@mailer)
  end

  if args.is_a?(Hash)
    ActionMailer.deprecator.warn <<~MSG
      Passing a Hash to the assert_enqueued_email_with :args kwarg causes the
      Hash to be treated as params. This behavior is deprecated and will be
      removed in Rails 7.2.

      To specify a params Hash, use the :params kwarg:

        assert_enqueued_email_with MyMailer, :my_method, params: { my_param: "value" }

      Or, to specify named mailer args as a Hash, wrap the Hash in an array:

        assert_enqueued_email_with MyMailer, :my_method, args: [{ my_arg: "value" }]
        # OR
        assert_enqueued_email_with MyMailer, :my_method, args: [my_arg: "value"]
    MSG

    params, args = args, nil
  end

  args = Array(args) unless args.is_a?(Proc)
  queue ||= mailer.deliver_later_queue_name || ActiveJob::Base.default_queue_name

  expected = ->(job_args) do
    job_kwargs = job_args.extract_options!

    [mailer.to_s, method.to_s, "deliver_now"] == job_args &&
      params === job_kwargs[:params] && args === job_kwargs[:args]
  end

  assert_enqueued_with(job: mailer.delivery_job, args: expected, queue: queue.to_s, &block)
end

#assert_enqueued_emails(number, &block) ⇒ Object

Asserts that the number of emails enqueued for later delivery matches the given number.

def test_emails
  assert_enqueued_emails 0
  ContactMailer.welcome.deliver_later
  assert_enqueued_emails 1
  ContactMailer.welcome.deliver_later
  assert_enqueued_emails 2
end

If a block is passed, that block should cause the specified number of emails to be enqueued.

def test_emails_again
  assert_enqueued_emails 1 do
    ContactMailer.welcome.deliver_later
  end

  assert_enqueued_emails 2 do
    ContactMailer.welcome.deliver_later
    ContactMailer.welcome.deliver_later
  end
end


91
92
93
# File 'actionmailer/lib/action_mailer/test_helper.rb', line 91

def assert_enqueued_emails(number, &block)
  assert_enqueued_jobs(number, only: ->(job) { delivery_job_filter(job) }, &block)
end

#assert_no_emails(&block) ⇒ Object

Asserts that no emails have been sent.

def test_emails
  assert_no_emails
  ContactMailer.welcome.deliver_now
  assert_emails 1
end

If a block is passed, that block should not cause any emails to be sent.

def test_emails_again
  assert_no_emails do
    # No emails should be sent from this block
  end
end

Note: This assertion is simply a shortcut for:

assert_emails 0, &block


63
64
65
# File 'actionmailer/lib/action_mailer/test_helper.rb', line 63

def assert_no_emails(&block)
  assert_emails 0, &block
end

#assert_no_enqueued_emails(&block) ⇒ Object

Asserts that no emails are enqueued for later delivery.

def test_no_emails
  assert_no_enqueued_emails
  ContactMailer.welcome.deliver_later
  assert_enqueued_emails 1
end

If a block is provided, it should not cause any emails to be enqueued.

def test_no_emails
  assert_no_enqueued_emails do
    # No emails should be enqueued from this block
  end
end


211
212
213
# File 'actionmailer/lib/action_mailer/test_helper.rb', line 211

def assert_no_enqueued_emails(&block)
  assert_enqueued_emails 0, &block
end

#capture_emails(&block) ⇒ Object

Returns any emails that are sent in the block.

def test_emails
  emails = capture_emails do
    ContactMailer.welcome.deliver_now
  end
  assert_equal "Hi there", emails.first.subject

  emails = capture_emails do
    ContactMailer.welcome.deliver_now
    ContactMailer.welcome.deliver_later
  end
  assert_equal "Hi there", emails.first.subject
end


269
270
271
272
273
274
275
# File 'actionmailer/lib/action_mailer/test_helper.rb', line 269

def capture_emails(&block)
  original_count = ActionMailer::Base.deliveries.size
  deliver_enqueued_emails(&block)
  new_count = ActionMailer::Base.deliveries.size
  diff = new_count - original_count
  ActionMailer::Base.deliveries.last(diff)
end

#deliver_enqueued_emails(queue: nil, at: nil, &block) ⇒ Object

Delivers all enqueued emails. If a block is given, delivers all of the emails that were enqueued throughout the duration of the block. If a block is not given, delivers all the enqueued emails up to this point in the test.

def test_deliver_enqueued_emails
  deliver_enqueued_emails do
    ContactMailer.welcome.deliver_later
  end

  assert_emails 1
end

def test_deliver_enqueued_emails_without_block
  ContactMailer.welcome.deliver_later

  deliver_enqueued_emails

  assert_emails 1
end

If the :queue option is specified, then only the emails(s) enqueued to a specific queue will be performed.

def test_deliver_enqueued_emails_with_queue
  deliver_enqueued_emails queue: :external_mailers do
    CustomerMailer.deliver_later_queue_name = :external_mailers
    CustomerMailer.welcome.deliver_later # will be performed
    EmployeeMailer.deliver_later_queue_name = :internal_mailers
    EmployeeMailer.welcome.deliver_later # will not be performed
  end

  assert_emails 1
end

If the :at option is specified, then only delivers emails enqueued to deliver immediately or before the given time.



251
252
253
# File 'actionmailer/lib/action_mailer/test_helper.rb', line 251

def deliver_enqueued_emails(queue: nil, at: nil, &block)
  perform_enqueued_jobs(only: ->(job) { delivery_job_filter(job) }, queue: queue, at: at, &block)
end