Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check what is queued in ActiveJob using Rspec

I'm working on a reset_password method in a Rails API app. When this endpoint is hit, an ActiveJob is queued that will fire off a request to Mandrill (our transactional email client). I'm currently trying to write the tests to ensure that that the ActiveJob is queued correctly when the controller endpoint is hit.

def reset_password
  @user = User.find_by(email: params[:user][:email])
  @user.send_reset_password_instructions
end

The send_reset_password_instructions creates some url's etc before creating the ActiveJob which's code is below:

class SendEmailJob < ActiveJob::Base
  queue_as :default

  def perform(message)
    mandrill = Mandrill::API.new
    mandrill.messages.send_template "reset-password", [], message
  rescue Mandrill::Error => e
    puts "A mandrill error occurred: #{e.class} - #{e.message}"
    raise
  end
end

At the moment we are not using any adapters for the ActiveJob, so I just want to check with Rspec that the ActiveJob is queued.

Currently my test looks something like this (I'm using factory girl to create the user):

require 'active_job/test_helper'

describe '#reset_password' do
  let(:user) { create :user }

  it 'should create an ActiveJob to send the reset password email' do
    expect(enqueued_jobs.size).to eq 0
    post :reset_password, user: { email: user.email }
    expect(enqueued_jobs.size).to eq 1
  end
end

Everything works in reality, I just need to create the tests!

I'm using ruby 2.1.2 and rails 4.1.6.

I can't see any documentation or help anywhere on the web on how to test on this so any help would be greatly appreciated!

like image 545
mylescc Avatar asked Oct 09 '14 09:10

mylescc


3 Answers

The accepted answer no longer works for me, so I tried Michael H.'s suggestion in the comments, which works.

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
like image 100
Josh Smith Avatar answered Oct 11 '22 15:10

Josh Smith


In a unit test, instead of checking what is queued one can also rely on ActiveJob working properly and just verify that it will be called by mocking its api.

 expect(MyJob).to receive(:perform_later).once 
 post :reset_password, user: { email: user.email }

The creators of the ActiveJob have used the same techniques for their unit tests. See GridJob Testobject

They create a testmock GridJob in their tests and override the perform method, so that it only adds jobs to a custom Array, they call JobBuffer. At the end they test, whether the buffer has jobs enqueued

At a single place one can ofc also do an integrations test. The ActiveJob test_helper.rb is supposed to be used with minitest not with rspec. So you have to rebuild it's functionalitity. You can just call

expect(ActiveJob::Base.queue_adapter.enqueued_jobs).to eq 1

without requiring anything

Update 1: As noticed within a comment. ActiveJob::Base.queue_adapter.enqueued_jobs works only by setting it the queue_adapter into test mode.

# either within config/environment/test.rb
config.active_job.queue_adapter = :test

# or within a test setup
ActiveJob::Base.queue_adapter = :test
like image 24
dre-hh Avatar answered Oct 11 '22 13:10

dre-hh


Rspec 3.4 now has have_enqueued_job cooked in, which makes this a lot easier to test:

it "enqueues a YourJob" do
  expect {
    get :your_action, {}
  }.to have_enqueued_job(YourJob)
end

it has other niceties for have_enqueued_job to allow you to check the argument(s) and the number of times it should be queued up.

like image 20
tirdadc Avatar answered Oct 11 '22 14:10

tirdadc