method

stub_chain

stub_chain(*methods)
public

No documentation available.

# File lib/spec/mocks/methods.rb, line 28
      def stub_chain(*methods)
        if methods.length > 1
          if matching_stub = __mock_proxy.find_matching_method_stub(methods[0])
            methods.shift
            matching_stub.invoke_return_block.stub_chain(*methods)
          else
            next_in_chain = Object.new
            stub!(methods.shift) {next_in_chain}
            next_in_chain.stub_chain(*methods)
          end
        else
          stub!(methods.shift)
        end
      end

3Notes

stub_chain is very useful when testing controller code

hosiawak · Mar 4, 20105 thanks

or any other chained method call type that you'd like to stub, example:

in your controller:

def new @user = current_site.users.new end

in your spec:

it "#new should assign a @user" do u = mock("User") controller.stub_chain(:current_site, :users, :new).and_return(u) assigns[:user].should == u end

whereas before you had to stub each chained method call separately:

it "#new should assign a @user" do u = mock("User") users = mock("Users collection", :new => u) site = mock("Site", :users => users) controller.stub!(:current_site).and_return(site) assigns[:user].should == u end

Please note that stub_chain was added to RSpec in version 1.2.6

Works only inside the "it" block

plugawy · Jul 21, 20102 thanks

Please note that +stub_chain+ doesn't work outside of the +it...do...end+ block.

If you need to create more complicated chains using a function you need to use the old way.

correct, but ..

aghyad · Jul 12, 20131 thank

stub_chain provides a very good replacement of long lines of nested stubs, but never forget it violates Law of Demeter; i.e. it indicates an increase of coupling in your classes and this is a bad thing because it means your objects now are making more unnecessary calls to other objects. for example:

def initialize(some_obj)
@obj = some_obj
end

def foo
@obj.x  # GOOD coupling - according to LoD you are allowed to call a method on your object
end

def bar
@obj.x.y   # BAD coupling - can not call a method on a returned value of another method call even if the initial call is legal
end

How is this related to TDD and stubs?

  • method foo test will have only one stub for a double of some_obj type
  • method bar will have 2 stubs: the first is going to swallow the other one to produce the result (and then can be shortened using this stub_chain technique)

Always remember: if your tests are using stub_chains --> your code is smelly and possibly tightly coupled.