Flowdock
method

stub_chain

Importance_3
RSpec latest stable (1.3.1) - 3 notes - Class: Spec::Mocks::Methods
stub_chain(*methods) public

Supports stubbing a chain of methods. Each argument represents a method name to stub, and each one returns a proxy object that can accept more stubs, until the last, which returns whatever is passed to +and_return_.

Examples

  # with this in an example ...
  article = double('article')
  Article.stub_chain(:authored_by, :published, :recent).and_return([article])
  # then this will return an Array with the article double in it:
  Article.authored_by(params[:author_id]).published.recent
Show source
Register or log in to add new notes.
March 4, 2010 - (>= 1.2.8)
5 thanks

stub_chain is very useful when testing controller code

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

July 21, 2010
2 thanks

Works only inside the "it" block

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.

July 12, 2013 - (1.1.4 - 1.3.1)
1 thank

correct, but ..

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.