Testing using RSpec, Part 1 - Models
October 28, 2008 at 10:33 AM · Posted under Blog · Tagged with rails, rspec, ruby, testing, unit
Models for me have always been a bit of an iffy area of testing. Why? I've had many discussions, both online and with my fellow colleagues about whether or not models should be mocked out. I've heard good points in both directions, but I think it's better to test the models against the database (if required), to pick up early mistakes in your code. Leaving testing the models against the database in acceptance can take longer, and also means possible bugs may not be picked up
until you write your acceptance tests. In some development environments, this simply isn't possible. However, for this article we're going to cover a single model with two different forms of testing: with and without database connectivity. Consider the model below:
class User < ActiveRecord::Base
def self.get_users_with_no_referrals
User.find(:all, :conditions => {:referral_count => 0})
end
end
Let's now test this, with database connectivity.
describe User do
before :each do
@user1 = User.create!( { :username => 'test', :referrals_count => 0 } )
@user2 = User.create!( { :username => 'another test', :referrals_count => 1 } )
end
it "should return all users with no referrals" do
users = User.get_users_with_no_referrals
users.length.should == 1
end
end
Let's look at this same example, but we'll mock out the database calls so as to avoid any talking to our database.
require 'ostruct'
describe User do
before :each do
@returned_users = [ OpenStruct.new( { :username => 'test', :referrals_count => 1 } ) ]
User.stub!(:get_users_with_no_referrals).and_return( @returned_users )
end
it "should return all users with no referrals" do
users = User.get_users_with_no_referrals
users.length.should == 1
end
end
Have a look closely here - I've purposely made a code mistake in order to demonstrate why I think it's better to test against the database with our models. Have a look at the @returned_users line - you'll notice that the referrals_count is set to 1. Now, due to the fact that we're actually mocking this out, our test will pass - and we'll be none the wiser. Had we checked this against the database, our test would fail - as no data would be in the database that would match our request.
There are advantages and disadvantages to doing both ways and I think more than anything it comes to developer discretion as to which method should best be used - some developers (like myself) simply prefer one way or the other - neither is particularly right or wrong, but one way does enforce "proper" unit testing, whereas the other enforces (in my opinion) a better test.
Update: Thanks to Alexandre for spotting this. Be sure to require 'ostruct' when using OpenStruct. I tend to use this heavily in all my projects, so forgot to include it in the article.
In my next article, I look at using RSpec for testing our controllers. Testing using RSpec, Part 2 - the Controller
Permalink