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.
This is a tricky thing in Rails. Michael Feathers sets some good
boundaries around unit testing in a blog entry:
http://www.artima.com/weblogs/viewpost.jsp?thread=126923. You'll note
that the first thing it says is "A test is not a unit test if: it
talks to a database."
So along comes Rails and Rails says "unit tests test models, which
talk to the database". So we have some confusion to get past right
away.
So let's say we agree that unit tests should never talk to a database.
If we agree on that definition, then we can agree that testing that
custom finds work correctly can not really be a unit test. So then the
question becomes not whether we should use the database for those
tests, but rather what we call those tests and where they go in our
test or spec directories.
One option is to leave them under test/units or spec/models. If the
time to run the suite isn't impeding your work, then this might be OK.
Another option is to separate out model specs that interact w/ the DB
from those that don't in two separate directories and set up rake
tasks to run all the non-db stuff or include the db stuff at your
whim.
Keep in mind that the important thing about all of this is that the
quicker you can run specs the more often you will, and tighter the
feedback loop, and the less time you'll spend debugging code that you
just wrote!
Hope that's helpful.
Cheers,
David
>> Pat
> _______________________________________________
> rspec-users mailing list
>rspec-users at rubyforge.org>http://rubyforge.org/mailman/listinfo/rspec-users>