When RSpec meets Validatable

Jay Fields writes an excellent module called Validatable to provide a class who include the module the ActiveRecord like behavior. For example, you can say validates_presence_of :attribute etc. This page has the RDoc of the module.When I write RSpec for a Validatable class, I ended up writing a stattement like this:

  @email.validate_only("presence_of/body")
  @email.errors.on(:body).should_not be_nil

Which is basically testing body is a required field. It works, but it looks complicated and hard to understand.The RSpec on Rails plugin provides a method error_on so that I can say something like this:

 model.should have(2).errors_on(:attribute)

This is much more readable than the previous one.I came up with a small extension on Validatable so that I can have a better syntax for testing Validatable.Here’s the spec:

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

module RspecValidatableSpec
  class Thing
    include Validatable
    attr_accessor :age
    attr_accessor :name
    validates_presence_of :age
    validates_numericality_of :age
  end
end

describe RSpecValidatable, 'a validatable object' do
  before :each do
    @thing = RspecValidatableSpec::Thing.new
  end

  it "should tell you it has at least one error on a field" do
    @thing.should have_at_least(1).error_on(:age)
  end

  it "should tell you it has one error on a required field" do
    @thing.should have(1).error_on_presence_of(:age)
  end

  it "should tell you it has one error on a numerical field" do
    @thing.should have(1).error_on_numericality_of(:age)
  end
end

describe RSpecValidatable, 'a validatable object' do
  before :each do
    @thing = RspecValidatableSpec::Thing.new
  end

  it "should tell you it has no error on a field" do
    @thing.should have(:no).errors_on(:name)
  end
end

You may need a different path to your spec_helper.rb to make it work.I got the “traditional” errors_on just like how RSpec works on ActiveRecords. What’s more, Validatable has a validates_only method which allows us validating only one type of error on one attribute. so I can have even better control than RSpec on Rails giving me over ActiveRecords. For example, I can specify what type of errors I expect to have.The only problem is that validates_only raises an error if you check an attribute on a validation that is not specified. So a nice spec like email.should have(:no).errors_on_presence_of(:attachment)is not quite possible. For those of you who are interested in this, here is the actually code:

module RSpecValidatable
  def errors_on(attribute)
    self.valid?
    [self.errors.on(attribute)].flatten.compact
  end

  def respond_to? method_name
    super(method_name) || method_name.to_s.index('error_on') == 0
  end

  def method_missing(error_on_method, attribute)
    reason = error_on_method.to_s.sub(/error_on_/, '')
    self.validate_only("#{reason.to_s}/#{attribute.to_s}")
    [self.errors.on(attribute)].flatten.compact
  end

  alias :error_on :errors_on

end

Validatable.send :include, RSpecValidatable
Advertisements

~ by Yi Wen on November 12, 2007.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: