Logo: Relish

  1. Sign in

Project: RSpec Mocks 3.0

Message Chains

You can use receive_message_chain in place of receive 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] }

Warning:

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.

Scenarios
Use `receive_message_chain` on a double
Given
a file named "receive_message_chain_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 "receive_message_chain_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

Last published almost 3 years ago by myronmarston.