Search code examples
ruby-on-rails-4capybaraactionmailerrspec3rails-activejob

Integration testing ActionMailer and ActiveJob


In the past, Rails 3, I've integrated action mailer testing with my Cucumber/Rspec-Capybara tests like the following example. With Rails 4, using the following doesn't seem to work.

I am able to see that the job is queued using enqueued_jobs.size. How do I initiate the enqueued job to ensure that the email subject, recipient and body are correctly being generated?

app/controllers/robots_controller.rb

class MyController < ApplicationController
  def create
    if robot.create robot_params
      RobotMailer.hello_world(robot.id).deliver_later
      redirect_to robots_path, notice: 'New robot created'
    else
      render :new
    end
  end
end

spec/features/robot_spec.rb

feature 'Robots' do
  scenario 'create a new robot' do
    login user
    visit '/robots'
    click_link 'Add Robot'
    fill_in 'Name', with: 'Robbie'
    click_button 'Submit'

    expect(page).to have_content 'New robot created'

    new_robot_mail = ActionMailer::Base.deliveries.last
    expect(new_robot_mail.to) eq '[email protected]'
    expect(new_robot_mail.subject) eq "You've created a new robot named Robbie"
  end
end

Solution

  • I use the rspec-activejob gem and the following code:

    RSpec.configure do |config|
      config.include(RSpec::ActiveJob)
    
      # clean out the queue after each spec
      config.after(:each) do
        ActiveJob::Base.queue_adapter.enqueued_jobs = []
        ActiveJob::Base.queue_adapter.performed_jobs = []
      end
    
      config.around :each, perform_enqueued: true do |example|
        @old_perform_enqueued_jobs = ActiveJob::Base.queue_adapter.perform_enqueued_jobs
        ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
        example.run
        ActiveJob::Base.queue_adapter.perform_enqueued_jobs = @old_perform_enqueued_jobs
      end
    
      config.around :each, perform_enqueued_at: true do |example|
        @old_perform_enqueued_at_jobs =    ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs
        ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
        example.run
        ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = @old_perform_enqueued_at_jobs
      end
    end
    

    This lets me tag features/scenarios with perform_enqueued: true if I want the jobs to actually execute.