有没有办法在 RSpec 到 return 双精度对象本身?

Is there a way in RSpec to return the double object itself on doubles?

在 RSpec 中是否有原生方法 return 双精度对象本身?

我希望 double(:relation, active: myself, in_batches: myself, each_record: [record])

显然可以通过分配和分布在多行上:

relation = double(:relation, each_record: [record])
allow(relation).to receive(:in_batches).and_return(relation)
allow(relation).to receive(:active).and_return(relation)

relation

被测代码,然后可以使用它作为方法链的替代,如 Foo.active.in_batches.each_record

我在 RSpec 文档和代码中找不到任何内容,但可能遗漏了一些重要的东西?

我不知道有这样的一行。很难提出任何建议,因为您不清楚您究竟打算如何使用这个 double...所以以下内容可能完全不适合您。

你提到

The code under test, can then use this as standin for method chains like Foo.active.in_batches.each_record.

也许您可以简化问题并测试方法的结果而不是实现细节?

假设这是被测方法:

def do_stuff
  Foo.active.in_batches.each_record do |foo|
    foo.update bar: 1
  end
end

你可以这样测试

let!(:active_foo) { create :foo, :active }
let!(:another_active_foo) { create :foo, :active }
let(:other_foo) { create :foo, :inactive }

specify do 
  expect { do_stuff }
    .to change(active_foo, :bar).from(nil).to(1)
    .and change(another_active_foo, :bar).from(nil).to(1)
end

specify do
  expect { do_stuff }.not_to change(other_foo)
end

但是(正如您可能已经注意到的那样)这样的测试存在一个小问题:可以像这样更改方法。

def do_stuff
  Foo.active.each do |foo|
    foo.update bar: 1
  end
end

而且测试仍然会通过。我会像这样添加断言:

expect(Foo).to receive_message_chain(:active, :in_batches, :each_record).and_call_original

明确传达这是必需的行为。

您可以通过receive_messages:

批量配置消息
relation = double(:relation, each_record: [record])
allow(relation).to receive_messages(in_batches: relation, active: relation)

还有 as_null_object 其中 returns 双重响应任意消息:

relation = double(:relation, each_record: [record]).as_null_object

relation.in_batches.active.each_record #=> [record]
relation.foo.bar.baz.each_record #=> [record]