【问题标题】:RSpec: how to chain receive().with()?RSpec:如何链接receive().with()?
【发布时间】:2022-01-03 09:39:03
【问题描述】:

我一直在用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)

【问题讨论】:

  • 恕我直言,您必须走很长的路。无论如何,我建议您过度考虑您的测试策略。目前,您测试是否调用了非常特定的方法组合,但如果这些方法调用实际上正在执行您想要的操作,则不会。相反,我将创建一个应该删除的示例记录(可能还有一些不应该删除的记录),然后运行该作业,然后测试是否只删除了预期的记录。经验法则是测试预期的结果,而不是具体的实现,因为在未来的版本中实现可能会发生变化或中断。
  • ^ 编写测试的一个主要原因是它为我们提供了重构代码的安全性。但是,如果您的测试强制执行特定的实现,那么它对于重构目的就变得毫无价值——更改代码意味着破坏测试。
  • @spickermann 你应该添加这个作为答案:)

标签: ruby-on-rails ruby rspec rspec-expectations


【解决方案1】:

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

不管怎样,我建议您多考虑一下您的测试策略。目前,您测试是否调用了非常具体的方法组合,但如果这些方法调用实际上正在执行您希望它们执行的操作,则不会。

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

例如这样:

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

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

【讨论】:

  • 感谢您和@stefan。我知道您来自哪里,并且我过去曾编写过这样的测试(测试要慢得多,因为创建的是记录,而不是存根)。但是,我也听说它说要让您的测试特定于范围和关注点。你应该存根或嘲笑外面的一切。例如。我不应该在这份工作中测试 Delayed::Job 的能力。这应该由系统规范进行测试。这只是意见分歧吗?
猜你喜欢
  • 2023-03-17
  • 2017-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多