RSpec on Rails: Models
In my last post I talked about using RSpec without Rails, but since just about all my Ruby programming involves Rails, I should probably get into how to specify Rails code.
RSpec on Rails: Models
So if you're new to RSpec and you want to get started quickly, head on over to rspec.rubyforge.org, get RSpec, Spec::Rails and then, after you've created a new Rails project and rspec-ed it (all detailed on the website) you can run:
Which will get you a bunch of stuff to play with. Of you'll need to set up your database.yml and run rake db:migrate if you want thing to work.
Fire up your favorite IDE and find the person_spec.rb and you should see something like:
Not the world's most rigorous set of specs, but then our model doesn't have much behavior at the moment. You'll notice that the auto-generated spec does use RSpec's built in interrogative feature. 'be_valid' makes RSpec look for 'valid?' Running the spec with spec formatting ('-fs') produces the following output:
But what's interesting is that if I remove the string between it and do, the output remains the same. If you leave off the name of a spec, RSpec guesses a name for you by looking at what you're asserting.
But let's say I want to specify that a Person is invalid without a name. Here's how I would do that:
As you can see, there's some built in sugar to make checking errors more readable. Now that I have a failing spec I can add the following line to my Person model to make the it pass:
Now let's do something a little more interesting:
What I'm saying here is that a Person should have a method that makes them rich and furthermore that the rich-making should happen inside a transaction. RSpec has mature mocking/stubbing framework built into it. 'and_yield' can even specify what to yield so you could return an object that you created in the spec and then check to make sure some things happened to it. Of course, before I get to much further, I should write some code to make the spec pass:
Now what if you already have a codebase that has a lot of Test::Unit tests but you want to start using rspec? Well you could just start writing specs in their own spec folder and rspec's default rails task will run both your tests and your specs. But that's not your only option. You can use RSpec inside your tests like so:
Now if you do this you won't have access to all the feature of RSpec: The reports, the mocking frameworks, and some custom matchers won't work (for instance '@person.should have(1).error_on(:name)' isn't available) but it's a low impact way of trying out RSpec before you commit.
Next time we'll look at view specing with RSpec.
RSpec on Rails: Models
So if you're new to RSpec and you want to get started quickly, head on over to rspec.rubyforge.org, get RSpec, Spec::Rails and then, after you've created a new Rails project and rspec-ed it (all detailed on the website) you can run:
ruby script/generate rspec_scaffold person name:string
phone_number:integer
cash:decimal
Which will get you a bunch of stuff to play with. Of you'll need to set up your database.yml and run rake db:migrate if you want thing to work.
Fire up your favorite IDE and find the person_spec.rb and you should see something like:
require File.dirname(__FILE__) + '/../spec_helper'
describe Person do
before(:each) do
@person = Person.new
end
it "should be valid" do
@person.should be_valid
end
end
Not the world's most rigorous set of specs, but then our model doesn't have much behavior at the moment. You'll notice that the auto-generated spec does use RSpec's built in interrogative feature. 'be_valid' makes RSpec look for 'valid?' Running the spec with spec formatting ('-fs') produces the following output:
Person
- should be valid
Finished in 0.028878 seconds
1 example, 0 failures
But what's interesting is that if I remove the string between it and do, the output remains the same. If you leave off the name of a spec, RSpec guesses a name for you by looking at what you're asserting.
But let's say I want to specify that a Person is invalid without a name. Here's how I would do that:
it "should be invalid without a name" do
@person.should have(1).error_on(:name)
@person.should_not be_valid
end
As you can see, there's some built in sugar to make checking errors more readable. Now that I have a failing spec I can add the following line to my Person model to make the it pass:
validates_presence_of :name
Now let's do something a little more interesting:
it "should run inside a transaction when making rich" do
Person.should_receive(:transaction).and_yield
@person.make_rich
@person.cash.should == 1000000.00
end
What I'm saying here is that a Person should have a method that makes them rich and furthermore that the rich-making should happen inside a transaction. RSpec has mature mocking/stubbing framework built into it. 'and_yield' can even specify what to yield so you could return an object that you created in the spec and then check to make sure some things happened to it. Of course, before I get to much further, I should write some code to make the spec pass:
def make_rich
Person.transaction do
self.cash = 1000000.00
end
end
Now what if you already have a codebase that has a lot of Test::Unit tests but you want to start using rspec? Well you could just start writing specs in their own spec folder and rspec's default rails task will run both your tests and your specs. But that's not your only option. You can use RSpec inside your tests like so:
require File.dirname(__FILE__) + '/../../test_helper'
require 'spec/test_case_adapter'
class PersonTest < person =" Person.new(:name"> "Jake")
end
def test_person_can_have_name_set
@person.name.should == "Jake"
end
end
Now if you do this you won't have access to all the feature of RSpec: The reports, the mocking frameworks, and some custom matchers won't work (for instance '@person.should have(1).error_on(:name)' isn't available) but it's a low impact way of trying out RSpec before you commit.
Next time we'll look at view specing with RSpec.
Comments