Message Chains

You can use receive_message_chain in place of receive in certain circumstances to stub a chain of messages:

 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 > 

Given any of these three forms:

 double.foo.bar # => :baz 

Common use in Rails/ActiveRecord:

 allow(Article).to receive_message_chain("recent.published")  [Article.new] > 

receive_message_chain is designed to be used with evaluating a response like and_return , and_yield etc. For legacy reasons, parity with stub_chain is supported but its uses are not considered good practice. Support for stub_chain parity may be removed in future versions.

Customisations like exactly (i.e. exactly(2).times ) are not supported.

Chains can be arbitrarily long, which makes it quite painless to violate the Law of Demeter in violent ways, so you should consider any use of receive_message_chain a code smell. Even though not all code smells indicate real problems (think fluent interfaces), receive_message_chain still results in brittle examples. For example, if you write allow(foo).to receive_message_chain(:bar, :baz => 37) in a spec and then the implementation calls foo.baz.bar , the stub will not work.

Chaining with receive_message_chain creates ambiguity in how the chains should be applied and applies design pressure on complex interactions in the implementation code. As such receive_message_chain is not a perfect replacement for receive . (see Issue 921 for a more detailed explanation). Other mocking methods like double and instance_double provide a better way of testing code with these interactions.

Use receive_message_chain on a double

Given a file named “receivemessagechain_spec.rb” with:

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 

When I run rspec receive_message_chain_spec.rb

Then the examples should all pass.

Use receive_message_chain on any instance of a class

Given a file named “receivemessagechain_spec.rb” with:

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 

When I run rspec receive_message_chain_spec.rb

Then the examples should all pass.

Brutus

Created with the assistance of Ninefold