November 9th, 2006
RSpec’s mocking/stubbing facilities are enhanced significantly in RSpec-0.7.
You can now intermingle stub methods and mock expectations on the same objects. This applies to instances of Spec::Mocks::Mock or ANY other object or class.
Having integrated stubs and mocks available not only supports isolated and fast controller specs, it also provides a nice way to separate setup noise from the “interesting bits” in your specs. I try to exploit this by preferring stub methods (stub!) in my setup and mock expectations (should_receive) in specify blocks.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
context "the PersonController" do controller_name :person setup do @person = mock("person") Person.stub!(:new).and_return(@person) end specify "should create a new person on GET to create" do Person.should_receive(:new).and_return(@person) get 'create' end specify "should assign new person to template on GET to create" do get 'create' assigns[:person].should_be @person end specify "should render 'person/create' on GET to create" do controller.should_render :template => "person/create" get 'create' end end
In this example, the line …
… in setup stubs the method so GET ‘create’ will work every time. The line …
… in the specify block “should create a new person on GET to create” sets an expectation that must be met. Even though it seems like duplication of the stub method, it is serving a different function: it tells the story for that spec.
Note how in the other specs there is no focus on Person.new. It’s out of the way, but the stub in the setup makes sure its taken care of.
So use stubs to take care of the “noise” (in this case stuff that has to be there to get the specs to run), and use mock expectations to put focus on the fact that something is expected.