On Aug 30, 1:32 pm, Justin Ko <jko...@gmail.com> 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. > _______________________________________________ > rspec-users mailing list > rspec-us...@rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
Just remembered another reason why I chose isolation. Sometimes, the setup to NOT isolate can be a real pain. a.k.a creating factories and relationships. But, let's say you've something like this in your controller: User.where(:email => 't...@blah.com').includes(:widgets).order('users.email ASC').limit(3) Simply stubbing or mocking that out would not spec the SQL very well. So what I usually do is extract it to the model: User.widgets_by_email('t...@blah.com') # in model def self.widgets_by_email(email) where(:email => email).includes(:widgets).order('users.email ASC').limit(3) end After doing that, I feel comfortable mocking out "User.widgets_by_email" in the controller, because all I care about is if its called or not. And of course, since the method has been moved to the model, you can unit spec the hell out of it :) _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users