larryzhao
larryzhao

Reputation: 3213

How to mock message chaining with RSpec?

I have a statement like Model.some_scope.pluck(:a_field) in a method I'd like to test.

What's the recommended way for me to stub the return value of this chained method call statement with rspec-mocks 3.4.0? I saw that stub_chain and receive_message_chain are both listed as old syntax on the RSpec website.

Best Regards.

Upvotes: 2

Views: 2592

Answers (1)

Dave Schweisguth
Dave Schweisguth

Reputation: 37607

The cleanest way to test that code is to extract a method, e.g.

class Model < ActiveRecord::Base
  def self.some_scope_fields
    some_scope.pluck(:a_field)
  end
end

That method can be stubbed without a chain.

However, there are times when it's neither convenient nor idiomatic to do that. For example, it's idiomatic, and not a Law of Demeter violation, to call create on an ActiveRecord model's association methods. In those cases,

  • if you don't care about method arguments, use receive_message_chain. stub_chain is deprecated; receive_message_chain is not. What the receive_message_chain documentation says is that "support for stub_chain parity may be removed in future versions". The issue linked to from that documentation makes it clear that "stub chain parity" means the use of with with receive_message_chain. So do use receive_message_chain; just don't use with with it.

  • if you do care about method arguments, use doubles. For example, for the code you gave,

    scope = double
    allow(scope).to receive(:pluck).with(:a_field) { # return whatever }
    allow(Model).to receive(:some_scope).with(no_args) { scope }
    

Upvotes: 3

Related Questions