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 actual 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