[rspec-users] We can't 100% remove our unit tests from the database, can we?

David Chelimsky dchelimsky at gmail.com
Thu Feb 22 17:32:21 EST 2007


On 2/22/07, aslak hellesoy <aslak.hellesoy at gmail.com> wrote:
> On 2/22/07, Pat Maddox <pergesu at gmail.com> wrote:
> > I hope this isn't too rambly.  This is sort of a brain dump of a
> > subject I've been thinking about for months as I've used RSpec.
> >
> > Let's say we've got a simple query method, that will find all the
> > users in the DB older than 18.  Our model could look like
> >
> > class User < ActiveRecord::Base
> >   def self.find_older_than(age)
> >     find :all, :conditions => ["age > ?", age]
> >   end
> > end
> >
> > What would our spec look like?
> >
> > specify "should find all users over the given age" do
> >   User.should_receive(:find).with(:all, :conditions => ["age > ?", 18])
> >   User.find_older_than 18
> > end
> >
> > I don't know about you, but to me that sucks.  There is no TDD rhythm
> > there.  We write a failing spec, but it's a fairly complex spec.  On
> > top of that we've basically implemented the method from within the
> > spec.  That's not TDD.
> >
> > We also can't refactor.  Let's say that at some point we decided to
> > change the method to
> >
> > def User.find_older_than(age)
> >   find(:all).select {|u| u.age > age }
> > end
> >
> > Our spec would break, even though the semantics of
> > User.find_older_than hasn't changed.
> >
> > Okay, so it's just a crappy spec.  But how do we change it?  Lots of
> > people suggest stubbing out DB calls.  There's no sense in testing the
> > same thing over and over - namely that you have a connection and your
> > ORM tool is working correctly.
> >
> > I'm beginning to think that most of the time, you don't want to use a
> > test DB at all.  When you're testing an AR model, just use an
> > in-memory record and stub any associations you need.  But when you do
> > do something that interacts with the database - like doing a custom
> > find - you need to use the database to make sure that you're getting
> > the right results.  Whether you use fixtures or create some records in
> > the setup method, you have to actually hit the db.
> >
> > Let me know what you think.  I'm completely open to the idea that I've
> > missed something pathetically obvious.
> >
>
> Only mock AR when you're speccing code that *interacts* with AR
> (controllers and views).
>
> Don't mock AR in specs for AR subclasses - let them go against a real database.
>
> Don't mock APIs you don't own.
> See http://www.jmock.org/oopsla2004.pdf for more on this
>
> HTH,
> Aslak

I agree w/ Aslak in principle.

The problem is that in an effort to make life seemingly easier for you
and me, AR forces us to couple our business logic with our persistence
logic, which puts us in a catch 22 vis a vis pre-Rails thinking about
testing and mocking. We can either test these things separately, which
requires that we mock code we didn't write, or we adhere to only
mocking code we don't own, which requires us to test two concepts
together.

Alternatively, we could add concepts to our rails apps to decouple
these things - non-AR model classes that delegate to AR backed models
- ModelFactories so we don't call class methods on the model classes,
etc. My instinct is that it would be more complicated that its worth.

Has anyone experimented w/ anything like that?

>
> > Pat
> > _______________________________________________
> > rspec-users mailing list
> > rspec-users at rubyforge.org
> > http://rubyforge.org/mailman/listinfo/rspec-users
> >
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>


More information about the rspec-users mailing list