Custom validation matcher
Thanks to FixtureReplacement my Rails model specs are looking awfully clean:
describe User, 'validating the login name' do
it 'should require it to be present' do
u = new_user(:login_name => nil)
u.should_not be_valid
u.should have_at_least(1).errors_on(:login_name)
end
# examples continue here...
end
Note how the new_user
method provided by FixtureReplacement allows me to focus only on the salient attributes for the example; in this case that’s the overridden login_name
attribute. Constructing new model instances is a breeze in this way, and the spec itself is much more intention-revealing because the salient bits and nothing else are right there up front.
And I’ve just whipped together a custom validation matcher to make it this kind of spec even clearer:
describe User, 'validating the login name' do
it 'should require it to be present' do
u = new_user(:login_name => nil)
u.should fail_validation_for(:login_name)
end
# another example, this one written on a single line
it 'should disallow trailing spaces' do
new_user(:login_name => 'foobar ').should fail_validation_for(:login_name)
end
# examples continue here...
end
Here’s the custom matcher, which I’ve stuck in my spec/spec_helper.rb
file:
module Spec
module Rails
module Matchers
class FailValidationFor # :nodoc:
def initialize attribute
@attribute = attribute
end
def matches? model
@model = model
!@model.valid? and !@model.errors.on(@attribute).nil?
end
def failure_message
"expected to fail validation with errors on #{@attribute} but was #{self.valid}; #{self.errors}"
end
def negative_failure_message
"expected to pass validation with no errors on #{@attribute} but was #{self.valid}; #{self.errors}"
end
def description
"fail validation for attribute #{@attribute}"
end
def valid
if @model.valid?
'valid'
else
'invalid'
end
end
def errors
if @model.valid?
'no errors'
else
@model.errors.full_messages.to_sentence
end
end
end # class FailValidationFor
def fail_validation_for attribute
FailValidationFor.new attribute
end
end # module Matchers
end # module Rails
end # module Spec