[rspec-users] Factories vs. stubs/mocks

Rob Biedenharn Rob at AgileConsultingLLC.com
Mon Aug 30 13:09:25 EDT 2010

On Aug 30, 2010, at 12:54 PM, Brennon Bortz wrote:
> On 30 Aug 2010, at 17:17, Justin Ko wrote:
>> On Aug 30, 11:59 am, Brennon Bortz <bren... at brennonbortz.com> wrote:
>>> I am, as usual, assigning an instance variable in a controller's  
>>> index action to find all instances of a model in the database.  I  
>>> want to write a spec that checks that this variable is assigned  
>>> correctly.  I can do:
>>> it "should provide a collection of widgets in @widgets" do
>>>      widget = Widget.create("title" => "my widget")
>>>      get :index
>>>      assigns[:widgets].should include(widget)
>>> end
>>> or:
>>> it "should provide a collection of widgets in @widgets" do
>>>      widget = Factory.create(:widget)
>>>      get :index
>>>      assigns[:widgets].should include(widget)
>>> end
>>> Is there a better way to do this with a mock model, though?
>>> Thanks,
>>> Brennon Bortz
>>> Software Researcher
>>> Dundalk Institute of Technology
>>> brennon.bo... at casala.ie
>>> Ph.D. Researcher & Composer - Sonic Arts Research Centre
>>> Queen's University, Belfast
>>> bren... at brennonbortz.com / bbort... at qub.ac.uk
>>> _______________________________________________
>>> rspec-users mailing list
>>> rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
>> Currently, what you're doing is checking that the Widget model  
>> returns
>> the correct widgets. If you want to isolate your controller spec from
>> the model, you must stub or mock the model method call in the action.
>> Example:
>> it "..." do
>> widget = mock_model(Widget)
>> Widget.should_receive(:all).and_return([widget])
>> get :index
>> assigns[:widgets].should include(widget)
>> end
>> To check that Widget.all does indeed return the correct widgets, I
>> would spec that behaviour in the Widget model spec.
>> Hope that helps.
> Hrm...that's exactly what I'd started with, and I was getting the  
> following error:
> Failures:
>  1) WidgetsController GET 'index' should provide a collection of  
> widgets in @widgets
>     Failure/Error: assigns[:widgets].should include(widget)
>     expected [] to include #<Widget:0x81686290 @name="Widget_1001">
> Stupidly, I had defined my controller method as:
> def index
>  @widgets = Widget.find(:all)
> end
> Changed that assignment to Widget.all...problem solved.

And THAT is the problem with using mocks (or stubs) for this.  You run  
the very real risk of specifying the implementation details in the  
structure of the spec/test.  There is a problem when you've limited  
the refactoring that can be done without altering the spec/test. Since  
Widget.find(:all) and Widget.all should always have the same result,  
the mere presence of the mock has cut off one possible refactoring.   
(The one that I distaste most limits changing .find(params[:id])  
to .find_by_id(params[:id]) by mocking/stubbing the call to find.)

Think about what really needs to be specified and be careful to mock/ 
stub as little as possible and to do so in a way that will limit  
opportunities to refactor the least.


Rob Biedenharn		
Rob at AgileConsultingLLC.com	http://AgileConsultingLLC.com/
rab at GaslightSoftware.com		http://GaslightSoftware.com/

More information about the rspec-users mailing list