Как проверить, что поставлено в очередь в ActiveJob, используя Rspec

Я работаю над методом reset_password в приложении Rails API. Когда эта конечная точка попадает, очередь ActiveJob ставится в очередь, которая сгорит запрос Mandrill (наш клиент транзакций электронной почты). В настоящее время я пытаюсь написать тесты, чтобы убедиться, что ActiveJob правильно поставлен в очередь при ударе конечной точки контроллера.

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

Send_reset_password_instructions создает некоторый url и т.д., прежде чем создавать ActiveJob, код которого ниже:

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

В настоящий момент мы не используем адаптеры для ActiveJob, поэтому просто хочу проверить с Rspec, что ActiveJob поставлен в очередь.

В настоящее время мой тест выглядит примерно так (я использую девушку factory для создания пользователя):

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

Все работает на самом деле, мне просто нужно создать тесты!

Я использую ruby ​​2.1.2 и rails 4.1.6.

Я не вижу никакой документации или помощи в любом месте в Интернете о том, как протестировать это, поэтому любая помощь будет принята с благодарностью!

Ответ 1

Принятый ответ больше не работает для меня, поэтому я пробовал предложение Майкла Х. в комментариях, который работает.

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

Ответ 2

Вам действительно не нужно тестировать функциональность ActiveJob. Просто проверьте, что ваш код правильно называет его, обрезая его

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

Создатели ActiveJob использовали те же методы для своих модульных тестов. См. Тестовый объект GridJob

Они создают тестовый образец GridJob в своих тестах и ​​переопределяют метод выполнения, так что он только добавляет задания к настраиваемому массиву, они вызывают JobBuffer, В конце они проверяют, имеет ли буфер задание в очереди

Однако, если ничто не мешает вам выполнить полный интеграционный тест. Предполагается, что ActiveJob test_helper.rb используется с minitest не с rspec. Поэтому вам нужно восстановить его функциональность. Вы можете просто позвонить

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

не требуя ничего

Обновление 1: Как замечено в комментарии. ActiveJob::Base.queue_adapter.enqueued_jobs работает только путем установки очереди queue_adapter в тестовый режим.

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

# or within a test setup
ActiveJob::Base.queue_adapter = :test

Ответ 3

Rspec 3.4 теперь has_enqueued_job готовят, что значительно облегчает тестирование:

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

у него есть другие тонкости для have_enqueued_job, чтобы вы могли проверить аргументы (аргументы) и количество раз, когда оно должно быть поставлено в очередь.

Ответ 4

Существует новое расширение rspec, которое облегчает вашу жизнь.

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

Ответ 5

Тестирование Rails ActiveJob с 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

Ответ 6

У меня были некоторые проблемы, возможно, потому что я не включил ActiveJob:: TestHelper, но это сработало для меня...

Во-первых, убедитесь, что у вас установлен адаптер очереди :test, как показано выше.

По какой-то причине clear_enqueued_jobs задания в блоке after не работали для меня, но источник показывает, что мы можем сделать следующее: 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