sethi
sethi

Reputation: 1889

Not able to use non stubbed method with receive_message_chain rspec

I have a strange issue with receive_message_chain rspec 3.6

 allow_any_instance_of(Order).to receive_message_chain('line_items.find') 
{LineItem.first}

When i do a order.line_items instead of returning a collection it returns me a <Double (anonymous)> object which is not what i wanted.

Any suggestions??

Upvotes: 0

Views: 986

Answers (1)

Laura Paakkinen
Laura Paakkinen

Reputation: 1691

When you tell RSpec to set receive_message_chain('line_items.find') on a specific object, it needs set the correct stubs in place for the chain to work.

First RSpec stubs the method line_items on the object. Next it needs to stub the method find, but this method needs to be stubbed on the return value of the method line_items. Since we stubbed that method, RSpec needs to come up with a some return value, that can be the target of the stubbing.

So RSpec sets the return value of the method line_items to be an anonymous double that has the the method find stubbed. Because of this once you set the receive_message_chain with 'line_items.find', the object will always return an anonymous double when line_items is called. And since you use allow_any_instance_of, this means that the method line_items is now stubbed on all the instances of Order.

I would recommend thinking about trying to remove the uses of allow_any_instance_of and receive_message_chain from your specs if it is possible, since they are really shortcuts to be used in specs if you really cannot change the design of your code. https://relishapp.com/rspec/rspec-mocks/docs/working-with-legacy-code/message-chains

However if that is not possible, you can remove the receive_message_chain and set the chain yourself.

line_items_stub = double 
# or line_items_stub = Order.new.line_items
# or line_items_stub = [LineItem.new, LineItem.new]
# or line_items_stub = the object that you want

allow_any_instance_of(Order).
  to receive(:line_items).
  and_return(line_items_stub)
allow(line_items_stub).to receive(:find)

If you were able to remove the allow_any_instance_of you could use and_call_original to call the for real implementation of line_items on that concrete instance you are stubbing the message_chain on.

Upvotes: 1

Related Questions