消息链
在某些情况下,可以使用 `receive_message_chain` 代替 `receive` 来存根消息链
allow(double).to receive_message_chain("foo.bar") { :baz }
allow(double).to receive_message_chain(:foo, :bar => :baz)
allow(double).to receive_message_chain(:foo, :bar) { :baz }
给定以下三种形式中的任何一种
double.foo.bar # => :baz
Rails/ActiveRecord 中的常见用法
allow(Article).to receive_message_chain("recent.published") { [Article.new] }
`receive_message_chain` 旨在与评估响应(例如 `and_return`、`and_yield` 等)一起使用。出于遗留原因,支持与 `stub_chain` 的一致性,但其用法不被视为最佳实践。对 `stub_chain` 一致性的支持可能会在将来的版本中删除。
不支持 `exactly` 之类的自定义项(即 `exactly(2).times`)。
警告:========
链可以任意长,这使得违反迪米特法则变得非常容易,因此您应该将任何使用 `receive_message_chain` 的情况视为代码异味。即使并非所有代码异味都表明存在实际问题(想想流畅的接口),`receive_message_chain` 仍然会导致脆弱的示例。例如,如果您在规范中写 `allow(foo).to receive_message_chain(:bar, :baz => 37)`,然后实现调用 `foo.baz.bar`,则存根将不起作用。
使用 `receive_message_chain` 进行链式操作会造成链式操作的应用方式的模糊性,并对实现代码中的复杂交互施加设计压力。因此,`receive_message_chain` 并不是 `receive` 的完美替代品。(有关更详细的解释,请参见 问题 921)。其他模拟方法(例如 `double` 和 `instance_double`)提供了一种更好的方法来测试具有这些交互的代码。
在替身上使用 `receive_message_chain`
给定一个名为 “receivemessagechain_spec.rb” 的文件,其中包含
RSpec.describe "Using receive_message_chain on a double" do
let(:dbl) { double }
example "using a string and a block" do
allow(dbl).to receive_message_chain("foo.bar") { :baz }
expect(dbl.foo.bar).to eq(:baz)
end
example "using symbols and a hash" do
allow(dbl).to receive_message_chain(:foo, :bar => :baz)
expect(dbl.foo.bar).to eq(:baz)
end
example "using symbols and a block" do
allow(dbl).to receive_message_chain(:foo, :bar) { :baz }
expect(dbl.foo.bar).to eq(:baz)
end
end
当我运行 `rspec receive_message_chain_spec.rb`
然后示例应该全部通过。
在类的任何实例上使用 `receive_message_chain`
给定一个名为 “receivemessagechain_spec.rb” 的文件,其中包含
RSpec.describe "Using receive_message_chain on any instance of a class" do
example "using a string and a block" do
allow_any_instance_of(Object).to receive_message_chain("foo.bar") { :baz }
expect(Object.new.foo.bar).to eq(:baz)
end
example "using symbols and a hash" do
allow_any_instance_of(Object).to receive_message_chain(:foo, :bar => :baz)
expect(Object.new.foo.bar).to eq(:baz)
end
example "using symbols and a block" do
allow_any_instance_of(Object).to receive_message_chain(:foo, :bar) { :baz }
expect(Object.new.foo.bar).to eq(:baz)
end
end
当我运行 `rspec receive_message_chain_spec.rb`
然后示例应该全部通过。