deploy
2010-Oct-23 13:04 UTC
[rspec-users] Spec''ing methods which essentially filter associations
I''ve been spec''ing for a long time, but I''ve never discovered the answer to this, so a definitive answer would be great! Whats the best way to spec a method which basically filters an association? class Thing < ActiveRecord::Base has_many :others def foos others.all(:conditions => { ... }) end end I don''t like the idea of creating a load of associated records in my spec and testing the returned result as I am not testing the associated model and as such would prefer any references to it to be mocked out, so the best I can some up with is this super lame test which is clearly way too implementation specific and essentially useless. describe Thing do describe ''foos'' do before :each do @thing = Thing.new @others_proxy = mock(''others proxy'') @others_proxy.stub!(:all) @thing.stub!(:others).and_return(@others_proxy) end it ''should return the associated others which meet the conditions'' do @others_proxy.should_receive(:all).with({ :conditions => '' ... '' }) @thing.foos end end end So what''s the best practice in this case? Pastie is here for pretty formatting: http://pastie.org/1242892 Thanks. P.S. This is old code I''ve inherited so I''m just trying to retrofit RSpec 1.3x to it before I start refactoring and bringing up to date with rails 3/rspec 2, so basically what I mean is what''s the answer if the model simply is how it is and can''t be changed?
David Chelimsky
2010-Oct-24 02:34 UTC
[rspec-users] Spec''ing methods which essentially filter associations
On Oct 23, 2010, at 11:04 AM, deploy wrote:> I''ve been spec''ing for a long time, but I''ve never discovered the > answer to this, so a definitive answer would be great! > > Whats the best way to spec a method which basically filters an > association? > > class Thing < ActiveRecord::Base > has_many :others > > def foos > others.all(:conditions => { ... }) > end > end > > I don''t like the idea of creating a load of associated records in my > spec and testing the returned result as I am not testing the > associated model and as such would prefer any references to it to be > mocked out, so the best I can some up with is this super lame test > which is clearly way too implementation specific and essentially > useless. > > describe Thing do > describe ''foos'' do > before :each do > @thing = Thing.new > @others_proxy = mock(''others proxy'') > @others_proxy.stub!(:all) > @thing.stub!(:others).and_return(@others_proxy) > end > > it ''should return the associated others which meet the conditions'' > do > @others_proxy.should_receive(:all).with({ :conditions => '' ... > '' }) > @thing.foos > end > end > end > > So what''s the best practice in this case?<tangent> There are no ''best practices'' absent _all_ of the context of the application, dev stack, dev team, other apps the team builds, etc. That aside ... </tangent> When it comes to spec''ing filters, I tend to just use ActiveRecord (or whatever ORM I''m using). It''s OK for some examples to hit the DB, and this is a good case for allowing it in my view, as the examples end up being more clear and readable and less brittle. HTH, David> Pastie is here for pretty formatting: http://pastie.org/1242892 > > Thanks. > > P.S. This is old code I''ve inherited so I''m just trying to retrofit > RSpec 1.3x to it before I start refactoring and bringing up to date > with rails 3/rspec 2, so basically what I mean is what''s the answer if > the model simply is how it is and can''t be changed?
deploy
2010-Oct-24 16:13 UTC
[rspec-users] Spec''ing methods which essentially filter associations
Thanks David, This is just indicative of a big problem I think all my tests have. I rarely/never use fixtures or the ORM for models that I''m not directly testing. I just can''t bring myself to do. Instead I tend to stub and mock things out like above. The consequence is that all my specs (of which there are a lot) are very tightly bound to the implementation rather than the behaviour and as such are very brittle. If a show action contains this (old Rails I know) @thing = Thing.find(:first, :order => "created_at DESC") I''ll end up stubbing it out with @thing = mock_model(Thing) Thing.stub!(:find).with(:first, :order => "created_at DESC").and_return(@thing) which obviously breaks as soon as someone refactors. I don''t know ... maybe I''m just not a natural tester :( Is everyone else using factories and fixtures these days or am I not alone? On Oct 24, 3:34?am, David Chelimsky <dchelim... at gmail.com> wrote:> On Oct 23, 2010, at 11:04 AM, deploy wrote: > > > > > > > > > > > I''ve been spec''ing for a long time, but I''ve never discovered the > > answer to this, so a definitive answer would be great! > > > Whats the best way to spec a method which basically filters an > > association? > > > class Thing < ActiveRecord::Base > > ?has_many :others > > > ?def foos > > ? ?others.all(:conditions => { ... }) > > ?end > > end > > > I don''t like the idea of creating a load of associated records in my > > spec and testing the returned result as I am not testing the > > associated model and as such would prefer any references to it to be > > mocked out, so the best I can some up with is this super lame test > > which is clearly way too implementation specific and essentially > > useless. > > > describe Thing do > > ?describe ''foos'' do > > ? ?before :each do > > ? ? ?@thing = Thing.new > > ? ? ?@others_proxy = mock(''others proxy'') > > ? ? ?@others_proxy.stub!(:all) > > ? ? ?@thing.stub!(:others).and_return(@others_proxy) > > ? ?end > > > ? ?it ''should return the associated others which meet the conditions'' > > do > > ? ? ?@others_proxy.should_receive(:all).with({ :conditions => '' ... > > '' }) > > ? ? ?@thing.foos > > ? ?end > > ?end > > end > > > So what''s the best practice in this case? > > <tangent> > There are no ''best practices'' absent _all_ of the context of the application, dev stack, dev team, other apps the team builds, etc. That aside ... > </tangent> > > When it comes to spec''ing filters, I tend to just use ActiveRecord (or whatever ORM I''m using). It''s OK for some examples to hit the DB, and this is a good case for allowing it in my view, as the examples end up being more clear and readable and less brittle. > > HTH, > David > > > Pastie is here for pretty formatting:http://pastie.org/1242892 > > > Thanks. > > > P.S. This is old code I''ve inherited so I''m just trying to retrofit > > RSpec 1.3x to it before I start refactoring and bringing up to date > > with rails 3/rspec 2, so basically what I mean is what''s the answer if > > the model simply is how it is and can''t be changed? > > _______________________________________________ > rspec-users mailing list > rspec-us... at rubyforge.orghttp://rubyforge.org/mailman/listinfo/rspec-users
David Chelimsky
2010-Oct-24 17:33 UTC
[rspec-users] Spec''ing methods which essentially filter associations
On Oct 24, 2010, at 2:13 PM, deploy wrote:> On Oct 24, 3:34 am, David Chelimsky <dchelim... at gmail.com> wrote: >> On Oct 23, 2010, at 11:04 AM, deploy wrote:>>> I''ve been spec''ing for a long time, but I''ve never discovered the >>> answer to this, so a definitive answer would be great! >> >>> Whats the best way to spec a method which basically filters an >>> association? >> >>> class Thing < ActiveRecord::Base >>> has_many :others >> >>> def foos >>> others.all(:conditions => { ... }) >>> end >>> end >> >>> I don''t like the idea of creating a load of associated records in my >>> spec and testing the returned result as I am not testing the >>> associated model and as such would prefer any references to it to be >>> mocked out, so the best I can some up with is this super lame test >>> which is clearly way too implementation specific and essentially >>> useless. >> >>> describe Thing do >>> describe ''foos'' do >>> before :each do >>> @thing = Thing.new >>> @others_proxy = mock(''others proxy'') >>> @others_proxy.stub!(:all) >>> @thing.stub!(:others).and_return(@others_proxy) >>> end >> >>> it ''should return the associated others which meet the conditions'' >>> do >>> @others_proxy.should_receive(:all).with({ :conditions => '' ... >>> '' }) >>> @thing.foos >>> end >>> end >>> end >> >>> So what''s the best practice in this case? >> >> <tangent> >> There are no ''best practices'' absent _all_ of the context of the application, dev stack, dev team, other apps the team builds, etc. That aside ... >> </tangent> >> >> When it comes to spec''ing filters, I tend to just use ActiveRecord (or whatever ORM I''m using). It''s OK for some examples to hit the DB, and this is a good case for allowing it in my view, as the examples end up being more clear and readable and less brittle.> Thanks David, > > This is just indicative of a big problem I think all my tests have. I > rarely/never use fixtures or the ORM for models that I''m not directly > testing. I just can''t bring myself to do. Instead I tend to stub and > mock things out like above. The consequence is that all my specs (of > which there are a lot) are very tightly bound to the implementation > rather than the behaviour and as such are very brittle. > > If a show action contains this (old Rails I know) > > @thing = Thing.find(:first, :order => "created_at DESC") > > I''ll end up stubbing it out with > > @thing = mock_model(Thing) > Thing.stub!(:find).with(:first, :order => "created_at > DESC").and_return(@thing) > > which obviously breaks as soon as someone refactors. > > I don''t know ... maybe I''m just not a natural tester :( > > Is everyone else using factories and fixtures these days or am I not > alone?(I moved your post inline to make it easier to follow the conversation.) To be clear, I tend to use stubs and message expectations in controller specs, but that is different from model specs that deal with filters. I also try to keep the model APIs high level, so rather than Thing.stub(:find).with(;first, :order ....), I''d say Thing.stub(:most_recent) and add that method to Thing. See the difference? There is an old saying that you should "mock your own APIs, not everyone else''s." This approach does not bind the examples to ActiveRecord or any other ORM, and if/when you want to switch, you just change the implementation within the methods you''ve added to the model. For model specs, I prefer to use factories over fixtures, and specify exactly what I need for example, interacting with the real database under the hood, but keeping the examples at a high level. In many cases, you can get away with not saving records (Factory.build(:widget) using FactoryGirl, for example), which I don''t believe is an option you get with Fixtures. HTH, David> >> >> HTH, >> David >> >>> Pastie is here for pretty formatting:http://pastie.org/1242892 >> >>> Thanks. >> >>> P.S. This is old code I''ve inherited so I''m just trying to retrofit >>> RSpec 1.3x to it before I start refactoring and bringing up to date >>> with rails 3/rspec 2, so basically what I mean is what''s the answer if >>> the model simply is how it is and can''t be changed?