1

当我在链中间需要更多粒度时,我一直在编写测试instance_doubles来代替消息链。但是,我想知道我是否正在以艰难的方式做事。

这是我要测试的方法:

def run_produceable_job
  # Delete any jobs that exist, but haven't started, in favor of this new job
  Delayed::Job.where(queue: 'produceable', locked_at: nil).delete_all

  ProduceableJob.perform_later
end

对于Delayed::Job呼叫,重要的是我检查队列名称是否符合预期。我还想确保 Delayed::Job.delete_all最后收到

我想做这样的事情:

expect(Delayed::Job).to receive(:where).with(queue: 'produceable', locked_at: nil).and_then_receive(:delete_all)
                                                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

RSpec 是否为接收提供某种链接?我浏览了文档,但找不到任何专门讨论添加多个接收的内容。

还是我必须走很长的路?

ar_relation = instance_double ActiveRecord::Relation
allow(Delayed::Job).to receive(:where).with(queue: 'produceable', locked_at: nil).and_return(ar_relation)
allow(ar_relation).to receive(:delete_all)

expect(Delayed::Job).to receive(:where).with(queue: 'produceable', locked_at: nil)
expect(ar_relation).to receive(:delete_all)
4

1 回答 1

2

恕我直言,你必须走很长的路。没有更短的方式来描述它。

无论如何,我建议您过度考虑您的测试策略。目前,您测试是否调用了非常特定的方法组合,但如果这些方法调用实际上正在执行您希望它们执行的操作,则不会。

相反,我会创建一个应该删除的示例记录(可能还有一些不应该删除的记录),然后运行该作业,然后测试是否只删除了预期的记录。

例如像这样:

let!(:record_to_be_deleted) { Delayed::Job.create!(queue: 'produceable', locked_at: nil) }
let!(:records_to_stay) do
  [ 
    Delayed::Job.create!(queue: 'produceable', locked_at: Time.current),
    Delayed::Job.create!(queue: 'default', locked_at: nil)
  ]
end

it "should remove only expected records" do
  expect {
    instance.run_produceable_job
  }.to chance { DelayedD::Job.count }.from(3).to(2)

  expect { 
    record_to_be_deleted.reload
  }.to raise_error(ActiveRecord::RecordNotFound)
end

经验法则是测试预期的结果,而不是具体的实现。因为实现可能会改变,将被重构或可能在未来的版本中中断。

于 2021-11-25T12:23:10.993 回答