I’ve been working on upgrading an app from Rails 2.3.11 to Rails 3.1. It was using the old rspec-on-rails-matchers plugin to get validators like:
it 'verifies that login is between 3 and 40 characters' do User.new.should validate_length_of(:login, :within => 3..40) end
I like that validation syntax, but lately when I see a plugins, I worry that I’m in for some archaeology. I did find a replacement gem, but it didn’t work on first try, so I read up on my options. I briefly considered shoulda, which is now compatible with rspec, but I settled on remarkable because it seems to match the syntax already in use. I wrote a little test rails app with a couple of models and was pleased the results.
Setup
rails new remarkable_app -T" cd remarkable_app
add to Gemfile:
group :development, :test do gem "rspec", "2.6.0" gem "rspec-rails", "2.6.1" gem "remarkable_activerecord", "4.0.0.alpha4" end
Then back on the command line, set up rspec:
rails g rspec:install
edit spec/spec_helper.rb to include (after require ‘rspec/rails’):
require 'remarkable/active_record'
Create a Model
Then make a model to play with (on the command line):
rails g model person name:string email:string rake db:migrate rake spec /Users/sarah/.rvm/rubies/ruby-1.9.2-p290/bin/ruby -S bundle exec rspec ./spec/models/person_spec.rb * Pending: Person add some examples to (or delete) /Users/sarah/src/mv/experiment/remarkable_app/spec/models/person_spec.rb # Not Yet Implemented # ./spec/models/person_spec.rb:4 Finished in 0.00026 seconds 1 example, 0 failures, 1 pending
yay! ready to get started test driving some remarkable validations…
Test Driven Validation
Let’s test drive our first validation..
describe Person do should_validate_length_of :name, :within => 3..40 end
note that remarkable also supports:
it { should validate_length_of :name, :within => 3..40 }
For my new code I’ll probably write the more concise version, but it is nice that my old code should work.
$ rake spec /Users/sarah/.rvm/rubies/ruby-1.9.2-p290/bin/ruby -S bundle exec rspec ./spec/models/person_spec.rb F Failures: 1) Person Failure/Error: send(should_or_should_not, send(method, *args, &block)) Expected Person to be invalid when name length is less than 3 characters # ./spec/models/person_spec.rb:4:in `block in <top (required)>' # ./spec/models/person_spec.rb:3:in `<top (required)>' Finished in 0.30367 seconds 1 example, 1 failure Failed examples: rspec /Users/sarah/.rvm/gems/ruby-1.9.2-p290@remarkable_rails3/gems/remarkable-4.0.0.alpha4/lib/remarkable/core/macros.rb:26 # Person rake aborted! ruby -S bundle exec rspec ./spec/models/person_spec.rb failed
add validation to my model…
class Person < ActiveRecord::Base validates_length_of :name, :within => 3..40 end
Test passes!
$ rake spec /Users/sarah/.rvm/rubies/ruby-1.9.2-p290/bin/ruby -S bundle exec rspec ./spec/models/person_spec.rb . Finished in 0.27601 seconds 1 example, 0 failures
More validations:
describe Person do should_validate_length_of :name, :within => 3..40 should_allow_values_for :email, "[email protected]" should_not_allow_values_for :email, "sarah", "@foo", "whatever.com" end
Reasonably nice error output:
$ rake spec /Users/sarah/.rvm/rubies/ruby-1.9.2-p290/bin/ruby -S bundle exec rspec ./spec/models/person_spec.rb ..F Failures: 1) Person Failure/Error: send(should_or_should_not, send(method, *args, &block)) Did not expect Person to be valid when email is set to "sarah" # ./spec/models/person_spec.rb:6:in `block in <top (required)>' # ./spec/models/person_spec.rb:3:in `<top (required)>' Finished in 0.29054 seconds 3 examples, 1 failure Failed examples: rspec /Users/sarah/.rvm/gems/ruby-1.9.2-p290@remarkable_rails3/gems/remarkable-4.0.0.alpha4/lib/remarkable/core/macros.rb:26 # Person rake aborted! ruby -S bundle exec rspec ./spec/models/person_spec.rb failed
Cheated a little on implementation by copying email validation from ActiveRecord docs… all good.
Now let’s add an association!
In app/model/person.rb:
describe Person do should_validate_length_of :name, :within => 3..40 should_allow_values_for :email, "[email protected]" should_not_allow_values_for :email, "sarah", "@foo", "whatever.com" should_have_many :addresses end
Watch it fail:
$ rake spec /Users/sarah/.rvm/rubies/ruby-1.9.2-p290/bin/ruby -S bundle exec rspec ./spec/models/person_spec.rb ...F Failures: 1) Person Failure/Error: send(should_or_should_not, send(method, *args, &block)) Expected Person records have many addresses, but the association does not exist # ./spec/models/person_spec.rb:7:in `block in <top (required)>' # ./spec/models/person_spec.rb:3:in `<top (required)>' Finished in 0.32615 seconds 4 examples, 1 failure
Make it pass:
$ rails g model address street:string person:belongs_to invoke active_record create db/migrate/20110721161741_create_addresses.rb create app/models/address.rb invoke rspec create spec/models/address_spec.rb $ rake db:migrate . Pending: Address add some examples to (or delete) /Users/sarah/src/mv/experiment/remarkable_app/spec/models/address_spec.rb # Not Yet Implemented # ./spec/models/address_spec.rb:4 Finished in 0.2998 seconds 5 examples, 0 failures, 1 pending
the rest is left as an exercise for the reader :)
Summary
Overall, I like the remarkable syntax. It seems reliable and on its way to a good release for Rails 3.