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