Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test ActionMailer deliver_later with rspec

trying to upgrade to Rails 4.2, using delayed_job_active_record. I've not set the delayed_job backend for test environment as thought that way jobs would execute straight away.

I'm trying to test the new 'deliver_later' method with RSpec, but I'm not sure how.

Old controller code:

ServiceMailer.delay.new_user(@user)

New controller code:

ServiceMailer.new_user(@user).deliver_later

I USED to test it like so:

expect(ServiceMailer).to receive(:new_user).with(@user).and_return(double("mailer", :deliver => true))

Now I get errors using that. (Double "mailer" received unexpected message :deliver_later with (no args))

Just

expect(ServiceMailer).to receive(:new_user)

fails too with 'undefined method `deliver_later' for nil:NilClass'

I've tried some examples that allow you to see if jobs are enqueued using test_helper in ActiveJob but I haven't managed to test that the correct job is queued.

expect(enqueued_jobs.size).to eq(1)

This passes if the test_helper is included, but it doesn't allow me to check it is the correct email that is being sent.

What I want to do is:

  • test that the correct email is queued (or executed straight away in test env)
  • with the correct parameters (@user)

Any ideas?? thanks

like image 257
bobomoreno Avatar asked Dec 25 '14 13:12

bobomoreno


People also ask

How do I run a test in RSpec?

To run a single Rspec test file, you can do: rspec spec/models/your_spec. rb to run the tests in the your_spec. rb file.

Is RSpec unit test?

RSpec is a unit test framework for the Ruby programming language. RSpec is different than traditional xUnit frameworks like JUnit because RSpec is a Behavior driven development tool. What this means is that, tests written in RSpec focus on the "behavior" of an application being tested.

What is RSpec testing?

RSpec is a testing tool for Ruby, created for behavior-driven development (BDD). It is the most frequently used testing library for Ruby in production applications. Even though it has a very rich and powerful DSL (domain-specific language), at its core it is a simple tool which you can start using rather quickly.


3 Answers

If I understand you correctly, you could do:

message_delivery = instance_double(ActionMailer::MessageDelivery)
expect(ServiceMailer).to receive(:new_user).with(@user).and_return(message_delivery)
allow(message_delivery).to receive(:deliver_later)

The key thing is that you need to somehow provide a double for deliver_later.

like image 101
Peter Alfvin Avatar answered Oct 23 '22 08:10

Peter Alfvin


Using ActiveJob and rspec-rails 3.4+, you could use have_enqueued_job like this:

expect { 
  YourMailer.your_method.deliver_later 
  # or any other method that eventually would trigger mail enqueuing
}.to( 
  have_enqueued_job.on_queue('mailers').with(
    # `with` isn't mandatory, but it will help if you want to make sure is
    # the correct enqueued mail.
    'YourMailer', 'your_method', 'deliver_now', any_param_you_want_to_check
  )
)

also double check in config/environments/test.rb you have:

config.action_mailer.delivery_method = :test
config.active_job.queue_adapter = :test

Another option would be to run inline jobs:

config.active_job.queue_adapter = :inline

But keep in mind this would affect the overall performance of your test suite, as all your jobs will run as soon as they're enqueued.

like image 22
Alter Lagos Avatar answered Oct 23 '22 07:10

Alter Lagos


If you find this question but are using ActiveJob rather than simply DelayedJob on its own, and are using Rails 5, I recommend configuring ActionMailer in config/environments/test.rb:

config.active_job.queue_adapter = :inline

(this was the default behavior prior to Rails 5)

like image 38
Gabe Kopley Avatar answered Oct 23 '22 07:10

Gabe Kopley