27

I have this code to test ActiveJob and ActionMailer with Rspec I don't know how really execute all enqueued job

describe 'whatever' do
  include ActiveJob::TestHelper

  after do
    clear_enqueued_jobs
  end  

  it 'should email' do
    expect(enqueued_jobs.size).to eq(1)
  end
end
Olivier
  • 493
  • 2
  • 6
  • 16

4 Answers4

28

The proper way to test will be to check number of enqueued jobs as in your example, and then test each job separately. If you want to do integration testing you can try perform_enqueued_jobs helper:

describe 'whatever' do
  include ActiveJob::TestHelper

  after do
    clear_enqueued_jobs
  end  

  it 'should email' do
    perform_enqueued_jobs do
      SomeClass.some_action
    end
  end
end

See ActiveJob::TestHelper docs

Rimian
  • 36,864
  • 16
  • 117
  • 117
Alex Ponomarev
  • 885
  • 5
  • 11
  • 3
    Can someone show a less contrived example of what this would look like with a mailer that accepts arguments? I can't seem to get this to work with something like `OrderMailer.receipt_email(order.id)`. – Chris Peters Mar 23 '15 at 15:55
  • Make sure you tack on the deliver_now or deliver_later method when testing a mailer and trying to fire the job. – David Melin Jun 28 '18 at 21:38
26

Here is how I solved a similar problem:

# rails_helper.rb
RSpec.configure do |config|
  config.before :example, perform_enqueued: true do
    @old_perform_enqueued_jobs = ActiveJob::Base.queue_adapter.perform_enqueued_jobs
    @old_perform_enqueued_at_jobs = ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs
    ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
    ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
  end

  config.after :example, perform_enqueued: true do
    ActiveJob::Base.queue_adapter.perform_enqueued_jobs = @old_perform_enqueued_jobs
    ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = @old_perform_enqueued_at_jobs
  end
end

Then in specs we can use:

it "should perform immediately", perform_enqueued: true do
  SomeJob.perform_later  
end
Rani Kheir
  • 1,049
  • 12
  • 15
timfjord
  • 1,759
  • 19
  • 20
  • Great answer. I added this to a file in spec/support. Seems that this is 100% necessary for testing email values in integration tests. – justingordon Feb 16 '16 at 05:15
  • Is this really the only way to ensure jobs are performed inline for specific integration tests? It blows my mind that there isn't a simpler way to do this. – cgat Jul 25 '18 at 22:06
  • 1
    @cgat No - you could place "ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true" in a before block, and your enqueued jobs will have been performed in time for your examples. – colemerrick Apr 23 '19 at 23:11
0

Just combined all the best pieces, +included sidekiq:

spec/support/perform_jobs.rb:

require 'sidekiq/testing'

RSpec.configure do |config|
  Sidekiq::Testing.fake!

  config.around(:each, perform_jobs: true) do |example|
    Sidekiq::Testing.inline! do
      queue_adapter = ActiveJob::Base.queue_adapter
      old_perform_enqueued_jobs = queue_adapter.perform_enqueued_jobs
      old_perform_enqueued_at_jobs = queue_adapter.perform_enqueued_at_jobs
      queue_adapter.perform_enqueued_jobs = true
      queue_adapter.perform_enqueued_at_jobs = true
      example.run
    ensure
      queue_adapter.perform_enqueued_jobs = old_perform_enqueued_jobs
      queue_adapter.perform_enqueued_at_jobs = old_perform_enqueued_at_jobs
    end
  end

end

spec/some_spec.rb:

it 'works', perform_jobs: true do
  ...
end
Lev Lukomsky
  • 6,346
  • 4
  • 34
  • 24
0

I have an :inline_jobs helper, where applied to a test it will perform the enqueued jobs and clear the enqueued jobs

module InlineJobHelpers
  def self.included(example_group)
    example_group.around(:each, :inline_jobs) do |example|
      perform_enqueued_jobs do
        example.run
      end
    ensure
      clear_enqueued_jobs
    end
  end
end

RSpec.configure do |config|
  config.include ActiveJob::TestHelper, :inline_jobs
  config.include InlineJobHelpers, :inline_jobs
end
Dorian
  • 7,749
  • 4
  • 38
  • 57