Rspec测试延迟任务(Delayed Job)

29
我在我的应用程序中有一些复杂的长时间运行的delayed_job进程。我正在使用Rspec测试用于进程的单个方法和类,但我也想使用不同的测试数据执行许多端到端后台作业。
我在delayed_job wiki上找不到任何相关信息,这个SO问题看起来很有趣,但我真的不明白这里发生了什么。 最佳的方式是什么,可以使用rSpec测试延迟的job链? 我可以轻松地使用工厂设置测试数据,然后调用启动后台处理的类。我希望测试需要很长时间才能完成。
编辑后台代码
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)如何告诉测试在验证结果之前等待延迟调用完成?

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

编辑

我应该补充说明,我得到了一个delayed_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 ==)

2
顺便提一下,看起来你正在混合使用 RSpec 的 shouldexpect 语法。expect(status.should == true) 应该是 status.should == true 或者 expect(status).to == true - fny
3个回答

42

有几种方法可以实现这个目标,所有的方法都要求在代码中执行作业。

方法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

方法二:一种测试方法,它禁用了队列并检查所需的结果。您可以通过在测试配置或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

然而,已知这种方法可能会导致回调问题。此处有更多细节。

方法三:编写观察程序,以监视创建的任何新作业并运行它们。这样,您就不必在测试中手动声明“work_off”。Artsy提供了这方面的代码片段

另外,最好在其他地方编写测试,以确保作业按预期排队。

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提供的第三种方法,您在哪里添加了对delayed_job_observer.rb的引用?是在spec_helper.rb中吗? - netwire
如果您使用较新的组织格式,请使用 rails_helper.rb,否则可以使用 spec_helper.rb - fny
在您的第一个方法中,工作不应该立即执行吗?它不会排队,对吧? - jshah
@jshah 这项工作不会立即执行,因为在测试期间没有后台工作程序来消耗该工作,除非出于某种疯狂的原因,您决定运行后台工作程序是个好主意。 - fny

16
如果您想在单个测试或一组测试周围运行延迟作业,可以将以下内容添加到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

0
我认为使用延迟任务的人都会遇到这个问题。在我的情况下,我通过使用延迟任务以异步模式发送电子邮件通知。在进行单元测试时,我通常需要验证邮件发送是否已经被排队执行。为了处理这个问题,我编写了一个自定义的Rspec匹配器,如下所示:
  RSpec::Matchers.define :have_created_delayed_job do |method_name|
    match do |_|
      Delayed::Job.all.find do |job|
        # rubocop:disable Security/YAMLLoad
        YAML.load(job.handler).method_name == method_name
        # rubocop:enable Security/YAMLLoad
      end
    end
    failure_message do |subject|
      "Expected that #{subject} would have created a delayed job with method name #{method_name}.\n
      But we didn't find the matching job."
    end
  end

然后在你的测试代码中,你可以这样做:
expect(subject).to have_created_delayed_job(:your_method_name)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接