web-dev-qa-db-fra.com

Comment vérifier ce qui est mis en file d'attente dans ActiveJob à l'aide de Rspec

Je travaille sur une méthode reset_password dans une application API Rails. Lorsque ce point de terminaison est atteint, un ActiveJob est mis en file d'attente qui enverra une demande à Mandrill (notre client de messagerie transactionnel). Je '' m essaie actuellement d'écrire les tests pour s'assurer que l'ActiveJob est correctement mis en file d'attente lorsque le point de terminaison du contrôleur est atteint.

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

Le send_reset_password_instructions crée quelques URL, etc. avant de créer l'ActiveJob dont le code est ci-dessous:

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

Pour le moment, nous n'utilisons aucun adaptateur pour l'ActiveJob, donc je veux juste vérifier avec Rspec que l'ActiveJob est mis en file d'attente.

Actuellement, mon test ressemble à ceci (j'utilise Factory Girl pour créer l'utilisateur):

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

Tout fonctionne en réalité, j'ai juste besoin de créer les tests!

J'utilise Ruby 2.1.2 et Rails 4.1.6.

Je ne vois aucune documentation ni aucune aide sur le Web sur la façon de tester cela, donc toute aide serait grandement appréciée!

33
mylescc

La réponse acceptée ne fonctionne plus pour moi, j'ai donc essayé la suggestion de Michael H. dans les commentaires, qui fonctionne.

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
44
Josh Smith

Vous n'avez vraiment pas besoin de tester la fonctionnalité ActiveJob. Testez simplement que votre code l'appelle correctement en le supprimant

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

Les créateurs de l'ActiveJob ont utilisé les mêmes techniques pour leurs tests unitaires. Voir GridJob Testobject

Ils créent un testmock GridJob dans leurs tests et remplacent la méthode perform, de sorte qu'il n'ajoute que des travaux à un tableau personnalisé, ils appellent JobBuffer . À la fin, ils testent si le tampon a des tâches en file d'attente

Cependant si rien ne peut vous empêcher de faire un test d'intégration complet. ActiveJob test_helper.rb est censé être utilisé avec minitest et non avec rspec. Vous devez donc reconstruire sa fonctionnalité. Vous pouvez simplement appeler

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

sans rien exiger

Mise à jour 1: Comme remarqué dans un commentaire. ActiveJob::Base.queue_adapter.enqueued_jobs fonctionne uniquement en le plaçant le queue_adapter en mode test.

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

# or within a test setup
ActiveJob::Base.queue_adapter = :test
34
dre-hh

Rspec 3.4 a maintenant have_enqueued_job cuit, ce qui rend cela beaucoup plus facile à tester:

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

il a d'autres subtilités pour have_enqueued_job pour vous permettre de vérifier les arguments et le nombre de fois où ils doivent être mis en file d'attente.

16
tirdadc

Test Rails ActiveJob avec RSpec

class MyJob < ActiveJob::Base
  queue_as :urgent

  rescue_from(NoResultsError) do
    retry_job wait: 5.minutes, queue: :default
  end

  def perform(*args)
    MyService.call(*args)
  end
end

require 'Rails_helper'

RSpec.describe MyJob, type: :job do
  include ActiveJob::TestHelper

  subject(:job) { described_class.perform_later(123) }

  it 'queues the job' do
    expect { job }
      .to change(ActiveJob::Base.queue_adapter.enqueued_jobs, :size).by(1)
  end

  it 'is in urgent queue' do
    expect(MyJob.new.queue_name).to eq('urgent')
  end

  it 'executes perform' do
    expect(MyService).to receive(:call).with(123)
    perform_enqueued_jobs { job }
  end

  it 'handles no results error' do
    allow(MyService).to receive(:call).and_raise(NoResultsError)

    perform_enqueued_jobs do
      expect_any_instance_of(MyJob)
        .to receive(:retry_job).with(wait: 10.minutes, queue: :default)

      job
    end
  end

  after do
    clear_enqueued_jobs
    clear_performed_jobs
  end
end
9
CharlesJHardy

Il y a une nouvelle extension rspec qui vous facilite la vie.

require 'Rails_helper'

RSpec.describe MyController do
  let(:user) { FactoryGirl.create(:user) }
  let(:params) { { user_id: user.id } }
  subject(:make_request) { described_class.make_request(params) }

  it { expect { make_request }.to enqueue_a(RequestMaker).with(global_id(user)) }
end
8
KARASZI István

J'ai eu quelques problèmes, peut-être parce que je n'ai pas inclus ActiveJob :: TestHelper, mais cela a fonctionné pour moi ...

Assurez-vous d'abord que l'adaptateur de file d'attente est défini sur :test comme le montrent les réponses ci-dessus.

Pour certaines raisons clear_enqueued_jobs les travaux dans le bloc after n'ont pas fonctionné pour moi, mais le source montre que nous pouvons faire ce qui suit: enqueued_jobs.clear

require 'Rails_helper'
include RSpec::Rails::Matchers

RSpec.describe "my_rake_task", type: :rake do

  after do
    ActiveJob::Base.queue_adapter.enqueued_jobs.clear
  end  


  context "when #all task is run" do
    it "enqueues jobs which have been enabled" do
      enabled_count = get_enabled_count
      subject.execute
      expect(ActiveJob::Base.queue_adapter.enqueued_jobs.size).to eq(enabled_count)
    end

    it "doesn't enqueues jobs which have been disabled" do
      enabled_count = get_enabled_count
      subject.execute
      expect(ActiveJob::Base.queue_adapter.enqueued_jobs.size).to eq(enabled_count)
    end
  end

end
2
Archernar