Rspec 测试延迟作业

Posted

技术标签:

【中文标题】Rspec 测试延迟作业【英文标题】:Rspec testing delayed_job 【发布时间】:2014-12-22 00:40:57 【问题描述】:

我的应用程序中有一些复杂、长时间运行的延迟作业进程。我正在使用 Rspec 来测试流程中使用的各个方法和类,但我也想使用不同的测试数据执行许多端到端的后台作业。

我在delayed_job wiki 上找不到任何关于此的内容,这个SO 问题看起来很有趣,但我并不真正了解这里发生了什么。 What's the best way to test delayed_job chains with rSpec?

我可以很容易地用工厂设置测试数据,然后调用启动后台处理的类。我预计测试需要很长时间才能完成。

编辑后台代码

class Singleplex
    def perform(batch_id,user)   
      batch = start_batch(batch_id,user)
        ... do lots of stuff ...
    end
    handle_asynchronously :perform, queue: :singleplex, :run_at => Proc.new  1.second.from_now 

spec/factories/batches.rb

FactoryGirl.define do
  factory :batch do
    batch_type 'singleplex'
    name 'valid panel'
    status 'ready'
  end

  factory :batch_detail do
    chrom 7
    chrom_start 140435012
    chrom_end 140435012
    target_offset 150
    padding 4
    primer3_parameter_id 1
    snp_mask 't'
    status 'ready'
    batch
  end
end

然后像这样运行测试

describe Batch do  
  it 'runs Singleplex for a valid panel' do
    batch = FactoryGirl.create(:batch)
    user = User.find(1)
    status =  Singleplex.new.perform(batch.id,user)
    expect(status.should == true)
  end
end

我有两个问题要解决:

1) 如何告诉测试等到 delay_job 调用完成后再验证结果?

2) 为了验证结果,我需要检查多个表中的值。在 Rspec 中执行此操作的最佳方法是什么?

编辑

我应该补充一下,我得到了一个 delay_job 对象,所以状态检查当然会失败。这些作业通常至少需要 10 分钟。

1) Batch runs Singleplex for a valid panel
     Failure/Error: expect(status.should == true)
       expected: true
            got: #<Delayed::Backend::ActiveRecord::Job id: nil, priority: 0, attempts: 0, handler: "--- !ruby/object:Delayed::PerformableMethod\nobject:...", last_error: nil, run_at: nil, locked_at: nil, failed_at: nil, locked_by: nil, queue: nil, created_at: nil, updated_at: nil> (using ==)

【问题讨论】:

顺便说一句,您似乎在混合使用 RSpec 的 shouldexpect 语法。 expect(status.should == true) 应该是 status.should == trueexpect(status).to == true 【参考方案1】:

有几种方法可以做到这一点。所有这些都要求您在代码中执行作业。

方法 1:将作业排队然后告诉DelayedJob::Worker 完成它的测试。

describe Batch do  
  it 'runs Singleplex for a valid panel' do
    batch = FactoryGirl.create(:batch)
    user = User.find(1)
    Singleplex.new.perform(batch.id,user)
    expect(Delayed::Worker.new.work_off).to eq [1, 0] # Returns [successes, failures]
    # Add expectations which check multiple tables to make sure the work is done
  end
end

方法 2:在禁用队列的情况下运行相关作业并检查所需结果的测试。您可以通过在测试配置中的某处或 before 块中调用 Delayed::Worker.delay_jobs = false 来延迟排队。

before(:each) do
  Delayed::Worker.delay_jobs = false
end
describe Batch do  
  it 'runs Singleplex for a valid panel' do
    batch = FactoryGirl.create(:batch)
    user = User.find(1)
    Singleplex.new.perform(batch.id,user)
    # expectations which check that the work is done
  end
end

不过,cause issues with callbacks 已经知道这种方法。

方法 3:编写一个观察者来监视任何创建的新作业并运行它们。这样您就不必在测试中手动声明“work_off”。 Artsy 有一个gist for this。

在其他地方进行测试以确保作业按预期排队也是一个好主意

it "queues welcome when a user is created" do
  expect(Delayed::Job.count).to eq 0
  # Create user step
  expect(Delayed::Job.count).to eq 1 # You should really be looking for the count of a specific job.
end

【讨论】:

感谢@faraz,对于方法3,您在哪里添加了对delayed_job_observer.rb 的引用,是在spec_helper.rb 中吗? 如果您使用较新的组织格式,请使用rails_helper.rb,否则spec_helper.rb 有效。 在您的第一种方法中,不应该立即执行作业,它不会排队吗? @jshah 由于在测试期间没有后台工作人员运行来消耗该工作,因此不会立即执行该工作,除非出于某种疯狂的原因,您认为运行后台工作人员是一个好主意。 【参考方案2】:

如果您想围绕单个测试或一组测试运行延迟作业,您可以将其添加到您的 spec_helper.rb

config.around(:each, :run_delayed_jobs) do |example|
  Delayed::Worker.delay_jobs = false

  example.run

  Delayed::Worker.delay_jobs = true
end

然后调用它:

it 'runs the job', :run_delayed_jobs do
  # delayed job magic
end

【讨论】:

以上是关于Rspec 测试延迟作业的主要内容,如果未能解决你的问题,请参考以下文章

使用 rSpec 测试延迟作业链的最佳方法是啥?

Rspec 如何测试 Sidekiq 作业是不是计划在特定时间运行

使用 RSpec 测试 DeserializationError

rspec 中未处理延迟作业

rspec rails testing:如何强制 ActiveJob 作业内联运行某些测试?

在 rspec 中使用 ActiveJob 执行挂起的作业