如何使用 Rspec 检查 ActiveJob 中排队的内容
Posted
技术标签:
【中文标题】如何使用 Rspec 检查 ActiveJob 中排队的内容【英文标题】:How to check what is queued in ActiveJob using Rspec 【发布时间】:2014-12-04 04:17:53 【问题描述】:我正在开发 Rails API 应用程序中的 reset_password 方法。当这个端点被命中时,一个 ActiveJob 被排队,它将向 Mandrill(我们的事务性电子邮件客户端)发出一个请求。我目前正在尝试编写测试以确保在命中控制器端点时 ActiveJob 正确排队。
def reset_password
@user = User.find_by(email: params[:user][:email])
@user.send_reset_password_instructions
end
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
目前我们没有为 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
现实中一切正常,我只需要创建测试!
我正在使用 ruby 2.1.2 和 rails 4.1.6。
我在网络上的任何地方都看不到有关如何对此进行测试的任何文档或帮助,因此我们将不胜感激!
【问题讨论】:
【参考方案1】:一个简单的解决方案是
# frozen_string_literal: true
class ApplicationJob < ActiveJob::Base
# Automatically retry jobs that encountered a deadlock
# retry_on ActiveRecord::Deadlocked
# Most jobs are safe to ignore if the underlying records are no longer available
# discard_on ActiveJob::DeserializationError
#
def self.my_jobs
enqueued_jobs.select|x| x['job_class'] == self.name
end
end
那么你可以在测试中使用辅助方法my_jobs
require 'rails_helper'
RSpec.describe SendBookingRemindersJob, type: :job do
describe '.start_time_approaching' do
let!(:booking) create :booking
it 'schedules 4 jobs' do
SendBookingRemindersJob.start_time_approaching(booking)
expect(SendBookingRemindersJob.my_jobs.count).to eq(4)
end
end
【讨论】:
【参考方案2】:我认为使用expect your_code .to have_enqueued_job(YourJob)
的解决方案非常干净,因为它们使用“官方”断言。如果你不喜欢长块传递给expect
,你也可以使用:
YourJob.perform_later
expect(YourJob).to have_been_enqueued
请在rubydoc documentation 中找到好的示例。
【讨论】:
【参考方案3】:在我看来,确保在执行请求时将作业排入队列很重要。 您可以通过以下解决方案做到这一点:
解决方案 1
expect post your_api_here, params: params, headers: headers
.to have_enqueued_job(YourJob)
.with(args)
解决方案 2
expect(YourJob).to receive(:perform_later).once.with(args)
post your_api_here, params: params, headers: headers
【讨论】:
【参考方案4】:在单元测试中,除了检查队列中的内容之外,还可以依赖 ActiveJob 正常工作,并通过模拟其 api 来验证它是否会被调用。
expect(MyJob).to receive(:perform_later).once
post :reset_password, user: email: user.email
ActiveJob 的创建者在他们的单元测试中使用了相同的技术。见GridJob Testobject
他们在测试中创建了一个 testmock GridJob 并覆盖了 perform 方法,因此它只会将作业添加到自定义数组中,他们调用 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
【讨论】:
@mylescc 我能够通过将 test_helper 包含在我的 Rspec.describe 块中来使其正常工作:include ActiveJob::TestHelper
ActiveJob::Base.queue_adapter.enqueued_jobs 不再起作用 =(
只要 config.active_job.queue_adapter = :test
在您的 config/environments/test.rb 文件中,针对 ActiveJob::Base.queue_adapter.enqueued_jobs
的测试就可以工作。
expect(ActiveJob::Base.queue_adapter.enqueued_jobs).to eq 1
应该是 expect(ActiveJob::Base.queue_adapter.enqueued_jobs.size).to eq 1
,缺少 .size
。
您使用的示例规范实际上不起作用。期望需要出现在传播调用的方法之前。【参考方案5】:
我遇到了一些问题,可能是因为我没有包含 ActiveJob::TestHelper,但这对我有用...
首先确保您将队列适配器设置为:test
,如上面的答案所示。
由于某种原因,after
块中的 clear_enqueued_jobs
工作对我不起作用,但 source 表明我们可以执行以下操作: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
【讨论】:
【参考方案6】:Rspec 3.4 现在已经加入了have_enqueued_job,这使得测试更容易:
it "enqueues a YourJob" do
expect
get :your_action,
.to have_enqueued_job(YourJob)
end
have_enqueued_job
还具有其他优点,可让您检查参数及其应排队的次数。
【讨论】:
谁觉得这有用,我用expect .to have_enqueued_job.on_queue('mailers')
来检查已发送的电子邮件。
优秀推荐!【参考方案7】:
Testing Rails ActiveJob with 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
【讨论】:
【参考方案8】:有一个新的rspec extension 让您的生活更轻松。
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
【讨论】:
【参考方案9】:接受的答案不再适合我,所以我在 cmets 中尝试了 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
【讨论】:
谢谢,帮助很大。有什么方法可以检查正确的作业是否排队?ActiveJob::TestHelper
ist 旨在与 minitest 而非 rspec 一起使用。它的代码充满了assert_equal
等。仅将其包含在一种方法中是个坏主意,恕我直言。这个模块中的方法enqueued_jobs
只是ActiveJob::Base.queue_adapter.enqueued_jobs
的快捷方式
@bobomoreno ActiveJob::Base.queue_adapter.enqueued_jobs
将允许您访问已排队的特定作业。
你也可以做expect your_action .to change(enqueued_jobs, :size).by n
,我喜欢用这个来测试没有工作已经入队,使用n = 0。你甚至不必使用这个方法在套件之后做clear_enqueued_jobs .以上是关于如何使用 Rspec 检查 ActiveJob 中排队的内容的主要内容,如果未能解决你的问题,请参考以下文章
rspec rails testing:如何强制 ActiveJob 作业内联运行某些测试?
使用 RSpec 测试 DeserializationError