On 30 Aug 2010, at 18:32, Justin Ko wrote: > > > On Aug 30, 1:09 pm, Rob Biedenharn <r...@agileconsultingllc.com> > wrote: >> 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...@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...@casala.ie >>>>> Ph.D. Researcher & Composer - Sonic Arts Research Centre >>>>> Queen's University, Belfast >>>>> bren...@brennonbortz.com / bbort...@qub.ac.uk >> >>>>> _______________________________________________ >>>>> rspec-users mailing list >>>>> rspec-us...@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 >> >> Rob Biedenharn >> r...@agileconsultingllc.com http://AgileConsultingLLC.com/ >> r...@gaslightsoftware.com http://GaslightSoftware.com/ >> >> _______________________________________________ >> rspec-users mailing list >> rspec-us...@rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users > > The method you suggested does benefit refactoring. However, by not > stubbing or mocking, your controller is no longer in isolation. Which > means if Widget.all breaks, the model AND controller spec will fail. > > The are pros and cons to both ways. But what has pushed me to the > isolation side is that without mocking/stubbing, you must create a > record in the database (via fixtures or factories) and it is much > slower.
Personally, I would use this as a driver to create a new method on Widget which did exactly what this controller wants, wrapping the details of the ActiveRecord API. That way, I have a nice, readable controller with fast, isolated, specs; a clear API on my models that makes sense in my domain; and if I want to change from a relational DB to a key-value store, I don't have a dependency on ActiveRecord leaking out of my model all over the place. > _______________________________________________ > rspec-users mailing list > rspec-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users cheers, Matt http://blog.mattwynne.net +44(0)7974 430184 _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users