This post is an overview of how I am using Rails3, rspec, and factory_girl to test my ActiveRecord models. Its not a complete or thorough tutorial, though I think I’ve covered most of the components and included some thoughts on the matter. It should serve its purpose, if not, ask your questions via the comment form.

I am working on another Rails 3 app. This one is a well defined app - its something I’d built in PHP before, and now I’m rebuilding in Rails 3. As such, I have a good idea of everything it should do.

Yes, this is the perfect scenario for using specification tests with RSpec.

Instead of fixtures, I’m using factories, more specifically, factory_girl.

What is Factory Girl?

Factory Girl (factory_girl) is a gem that can be used to create objects. The process is quite similar to using fixtures, though the format and syntax is different.

Examples help, so here we go! A quick overview:

The app I am building is a DNS management tool. The base models are "zone" and "record". The zone model is the origin part of a zonefile, and includes a field named origin. For a single DNS server, the origin must be unique.

The Zone Model

Here is the zone model, including a couple, but not all, validations:

class Zone < ActiveRecord::Base
  validates :origin,
    :presence => true,
    :uniqueness => true

  validates :ttl,
    :numericality => true
end

The Zone Factory

Here is the zone factory, located in spec/support/zone.rb:

Factory.define :zone, :class => Zone do |f|
  f.id 1
  f.origin 'example.com.'
  f.ttl 14400
  f.mbox 'support@example.com'
  f.ns 'ns.example.net'
end

Note: this is really what I like about factories - they feel a lot more like Ruby than YAML.

The Model Test

Here is the zone model spec test, which was initially auto-generated by rails, and of course actually written by yours truly:

require 'spec_helper'
describe Zone do
  before(:all) do
    @zone = Factory.build(:zone)
    @zone.save
  end
  it "should be able to create a new zone" do
    puts @zone.errors.inspect
    @zone.should be_valid
  end
  it "should require origin to be unique" do
    zone = Zone.new(:id => 2, :origin => 'example.com.')
    zone.save
    zone.should_not be_valid
    zone.errors[:origin].should == ["has already been taken"]
  end
  it "should not update with invalid data" do
    @zone.update_attributes({:ttl => 'kasjdfkhd'})
    @zone.should_not be_valid
  end
  after(:all) do
    @zone.delete
  end
end

I run these tests with: rake spec.

I hope this quick overview helps - I’m off to write more model tests. :-)