I introduced the dev team at Izea (then only 2 people – me and Dray) to RSpec pretty much as soon as it became available. It looked interesting, but I have to admit that it’s not something that caught on with me immediately. I’ve been a big fan of Test Driven Development for many years, but the switch to Behavior Driven just felt a little odd. I still don’t think I’ve fully gotten the ‘thinking’ behind the paradigm shift down, but I did start to pay more attention to RSpec again recently.
RSpec 1.1.12 now works with the Shoulda macros, and that single fact now has me as a convert to the world of RSpec for all the work I’m doing in Ruby (which, it may surprise you, is quite a lot lately).
From the Shoulda website…
The Shoulda gem makes it easy to write elegant, understandable, and maintainable Ruby tests. Shoulda consists of test macros, assertions, and helpers added on to the Test::Unit framework. It’s fully compatible with your existing tests, and requires no retooling to use.
- Helpers – context and should give you rSpec like test blocks. In addition, you get nested contexts and a much more readable syntax.
- Macros – Generate many ActionController and ActiveRecord tests with helpful error messages. They get you started quickly, and can help you ensure that your application is conforming to best practices.
- Assertions – Many common Rails testing idioms have been distilled into a set of useful assertions.
Within the project I’m working on, we needed to get a set of tests built that just made sure required validations were on an underlying model. There’s an argument that says you don’t need to do that, that you should just let ActiveRecord do what it does while you get on with testing your app, but we really needed to have some tests in place just to make sure over time that required validations don’t magically disappear. However, I do tend to agree with the counter argument, and the amount of code required to write the RSpec tests really didn’t help at all.
With Shoulda, it became very simple. For example, here’s a full set of RSpec specifications (?) using Shoulda to check that uniqueness constraints are still applied to a basic User model.
describe "To keep the User table clean," do
before(:each) { create_user() }
subject { User.new }
it {should validate_uniqueness_of(:login)}
it {should validate_uniqueness_of(:name)}
it {should validate_uniqueness_of(:email)}
end
“validate_uniqueness_of” is one of many Shoulda macros. It requires a record to already be in the database and then goes ahead and tries to create a new one with the specified field set to the same value as previously to check that the specification still holds.
The “before” block calls a method to create a simple user record before each “it” block runs. The “subject” block returns the object that each Shoulda macro works with. Simple huh. The macros also output nice and legibly in specdoc format.
To keep the User table clean,
- should require case sensitive unique value for login
- should require case sensitive unique value for name
- should require case sensitive unique value for email
Here’s another set of tests on the same model, this time checking data formats using the Shoulda macros.
describe "New users" do
subject{ User.new }
# Presence tests - login, email and name all need to be present
it {should validate_presence_of(:login)}
it {should validate_presence_of(:email)}
it {should validate_presence_of(:name)}
# Length tests - there are length constraints on login and name
it {should ensure_length_of(:login).is_at_least(3).is_at_most(40)}
it {should ensure_length_of(:name).is_at_most(100)}
# Format tests - login and email both need to comply to a format.
%w(test 1234 test23 a1.-_@).each do |value|
it {should allow_value(value).for(:login)}
end
%w(test@test.com me_test@test.com test@sub.test.com).each do |value|
it {should allow_value(value).for(:email)}
end
end
In the case of these tests, we don’t need a pre-existing record for the Shoulda macros to work their magic.
Setting the whole thing up in an existing Rails project was quite easy, but it did take me a little while to figure out which gem to use etc. Here’s the process.
sudo gem install thoughtbot-shoulda --source=http://gems.github.com
rake gems:install
rake gems:unpack
I also had to add a line to environment.rb
config.gem "thoughtbot-shoulda", :lib => "shoulda", :source => "http://gems.github.com"
I’m sure part of the argument against testing such basic functionality of a model stems from the fact that RSpec code can be so horribly verbose and time consuming to produce. Shoulda makes it a snap though and having tests ensure that these basic validations are in place, no matter what refactorings happen over time, is a safety net I’m glad to have.
Shoulda also lets you produce your own macros with a similar feel, but I haven’t dived into that just yet.