如何使用Rspec检查ActiveJob中排队的内容

myl*_*scc 34 ruby rspec ruby-on-rails rails-activejob

我正在使用Rails API应用程序中的reset_password方法.当该端点被命中时,ActiveJob将排队,该命令将触发对Mandrill(我们的交易电子邮件客户端)的请求.我正在尝试编写测试以确保在控制器端点被命中时ActiveJob正确排队.

def reset_password
  @user = User.find_by(email: params[:user][:email])
  @user.send_reset_password_instructions
end
Run Code Online (Sandbox Code Playgroud)

send_reset_password_instructions在创建ActiveJob之前创建了一些url等,其代码如下:

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
Run Code Online (Sandbox Code Playgroud)

目前我们没有为ActiveJob使用任何适配器,所以我只想检查Rspec ActiveJob是否排队.

目前我的测试看起来像这样(我正在使用工厂女孩来创建用户):

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
Run Code Online (Sandbox Code Playgroud)

一切都在现实中,我只需要创建测试!

我正在使用ruby 2.1.2和rails 4.1.6.

我无法在网上任何地方看到任何文档或帮助如何测试,所以任何帮助将不胜感激!

Jos*_*ith 46

接受的答案对我来说不再适用,所以我在评论中尝试了Michael H.的建议.

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
Run Code Online (Sandbox Code Playgroud)

  • `ActiveJob :: TestHelper`旨在与minitest而不是rspec一起使用.它的代码充满了`assert_equal`等.包含它只适用于一种方法是个坏主意,imho.这个模块中的方法`enqueued_jobs`只是`ActiveJob :: Base.queue_adapter.enqueued_jobs`的快捷方式. (9认同)
  • @bobomoreno `ActiveJob::Base.queue_adapter.enqueued_jobs` 将使您可以访问已排队的特定作业。 (3认同)

dre*_*-hh 35

您真的不需要测试ActiveJob功能.只需测试一下代码是否正确调用它即可

 expect(MyJob).to receive(:perform_later).once 
 post :reset_password, user: { email: user.email }
Run Code Online (Sandbox Code Playgroud)

ActiveJob的创建者使用相同的技术进行单元测试.请参阅GridJob Testobject

他们在测试中创建了一个testmock GridJob并覆盖了perform方法,因此它只将作业添加到自定义数组中,它们调用JobBuffer.最后他们测试缓冲区是否有作业入队

但是,如果没有什么不能阻止你做一个完整的集成测试.ActiveJob test_helper.rb应该与minitest一起使用而不是与rspec一起使用.所以你必须重建它的功能.你可以打电话

expect(ActiveJob::Base.queue_adapter.enqueued_jobs).to eq 1
Run Code Online (Sandbox Code Playgroud)

不需要任何东西

更新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
Run Code Online (Sandbox Code Playgroud)

  • ActiveJob :: Base.queue_adapter.enqueued_jobs不再起作用=( (3认同)
  • 只要`config.active_job.queue_adapter =:test`在config/environments/test.rb文件中,对`ActiveJob :: Base.queue_adapter.enqueued_jobs`的测试就可以正常工作. (3认同)
  • `expect(ActiveJob :: Base.queue_adapter.enqueued_jobs).to eq 1`应该是`expect(ActiveJob :: Base.queue_adapter.enqueued_jobs.size).to eq 1`,缺少`.size`. (3认同)
  • @mylescc 我能够通过将 test_helper 包含在我的 Rspec.describe 块中来使其工作:`include ActiveJob::TestHelper` (2认同)
  • 您使用的示例规范实际上不起作用.期望需要出现在传播调用的方法之前. (2认同)

tir*_*adc 16

Rspec 3.4现在已经熟化了have_enqueued_job,这使得测试更加容易:

it "enqueues a YourJob" do
  expect {
    get :your_action, {}
  }.to have_enqueued_job(YourJob)
end
Run Code Online (Sandbox Code Playgroud)

它有其他细节have_enqueued_job可以让你检查参数和它应该排队的次数.

  • 无论谁发现这个有用,我都使用`expect {} .to have_enqueued_job.on_queue('mailers')来查看已发送的电子邮件. (5认同)

Cha*_*rdy 9

使用RSpec测试Rails ActiveJob

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
Run Code Online (Sandbox Code Playgroud)


KAR*_*ván 8

有一个新的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
Run Code Online (Sandbox Code Playgroud)


gia*_*pnh 6

在我看来,确保执行请求时作业已排队很重要。您可以使用以下解决方案来做到这一点:

解决方案1

expect{ post your_api_here, params: params, headers: headers }
 .to have_enqueued_job(YourJob)
 .with(args)
Run Code Online (Sandbox Code Playgroud)

解决方案2

expect(YourJob).to receive(:perform_later).once.with(args)
post your_api_here, params: params, headers: headers

Run Code Online (Sandbox Code Playgroud)


小智 5

我遇到了一些问题,也许是因为我没有包含 ActiveJob::TestHelper,但这对我有用...

首先确保您将队列适配器设置:test为如上答案所示。

出于某种原因clear_enqueued_jobsafter块中的工作对我不起作用,但来源显示我们可以执行以下操作: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
Run Code Online (Sandbox Code Playgroud)