An Example of RSpec

Intense

I've taken a real shining to Behaviour Driven Development since working on my last project. BDD is the next evolution of Test Driven Development, and basically you think about all the behaviours of your system first. Then you write them down (key step here!), and then make your application meet these define behaviours. On a couple of my side projects, I've started using RSpec to implement some BDD.

When I first started out, I noticed a great void of coherent examples on how to use RSpec. Partially because RSpec was changing which broke old tutorials and tests, and partially because the people writing stuff down were so far out on the edge, that what they had didn't help me out in the slightest. I'm throwing this out there, so that you can see how easy it can be to add a specification test to your rails application.

You'll need to have rails, and rspec installed for this to work. Roll out a vanilla rails application and install the rspec plugins:


# rails myapp
# cd myapp
# ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_1_0_0/rspec
# ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_1_0_0/rspec_on_rails

Add the -x flag to the install command if your code is already in subversion so that svn:extern is used and you can pick up RSpec updates. Now, we need to bootstrap rspec, and create the user object we are going to be writing specifications for:


# ruby script/generate rspec
# ruby script/generate rspec_model user

Now that all the monkey work is done, we can get down to business, I quickly modified the db/migrate/001_create_user.rb to read:


class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.column "identity", :string
    end
  end

  def self.down
    drop_table :users
  end
end

Boring, eh? Well I'm trying to keep it that way. These examples can really get out of hand, if you aren't careful. What we have is the simplest user model ever, all it's got is an identity column. Within my application, I would like it to be impossible for a user to exist without an identity. Simple enough, so I modify /spec/models/user_spec.rb to be:


require File.dirname(__FILE__) + '/../spec_helper'

describe User, " in General" do
  before(:each) do
    @users = User.new
    #Identity is a required field
    @users.identity = "http://whatever.com"
  end

  it "should be valid" do
     @users.should be_valid
  end

  it "should be invalid without an identity" do
    @users.identity = nil
    @users.should_not be_valid
  end
end

The first specification is so trivial it appears silly. It is a valid (and it is already in the generated *_spec.rb file) specification, and will blow up if people change what it means for your model to be valid without updating the specification. The rest almost reads itself. You can see we are describing a User, " in General" and that we have two specification, it should be valid, and it should be invalid without and identity. Pretty simple, eh? Well, let's keep it that way. You can now run the specification test:


# rake spec

Uh, oh! FAILURES! Written in red (on colour TTY) is 'User in General should be invalid without an identity' FAILED. Well, of course, we haven't written any code to invalidate a user without a identity; up to this point, we've only been writing specifications. To make our application meet the meager specifications I changed the app/models/user.rb to be:


class User < ActiveRecord::Base
  validates_presence_of :identity, :message => "Identity is required"
end

Now if I run the rake spec again, I can see that my application is up to specification. That's it. Done, Kaput, and still useless. But, at least we have a starting point. The next steps would be to write down more specifications, and then make the application pass those specifications tests. Repeat, until you have an application that meets all your specifications. Some other nifty things that rspec can bring to the table are rake spec:doc which writes out some bullet point specification (Great to give to the pointy haired manager when he comes askin' what specification to which you were building the application. rake spec:rcov will give you a coverage report of your specification tests (in case you cheated and went ahead writing code without specifying it's behaviour). Already my application has a pile of specifications written for it, and it's weird how you just write better code when you stop to think what you really want it to do first.

Leave a Comment