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?

On Oct 23, 2010, at 11:04 AM, deploy wrote:

others.all(:conditions => { … })
describe Thing do
@others_proxy.should_receive(:all).with({ :conditions => ’ …
’ })
@thing.foos
end
end
end

So what’s the best practice in this case?

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 ...

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

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 :frowning:

Is everyone else using factories and fixtures these days or am I not
alone?

On Oct 24, 2010, at 2:13 PM, deploy wrote:

On Oct 24, 3:34 am, David C. [email protected] wrote:

On Oct 23, 2010, at 11:04 AM, deploy wrote:

others.all(:conditions => { … })
describe Thing do
@others_proxy.should_receive(:all).with({ :conditions => ’ …

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.

I don’t know … maybe I’m just not a natural tester :frowning:

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