Testing Custom Rails Validations with Shoulda
In this post, I’ll show how to write custom validations for Rails’ models, and how to test them with Shoulda, the ‘Makes tests easy on the fingers and the eyes’ testing plugin.
Rails’ ActiveRecord validations are great when you want to make sure attributes are the way you want them. But what about the use-cases where you need to write a custom one?
Creating the Validation
The validation we’ll write is called validates_positive_or_zero
, which will return an error if a relevant integer attribute is negative.
Create the file RAILS_ROOT/lib/validations.rb
with the following contents:
def validates_positive_or_zero(*attr_names)
# Set the default error message.
configuration = { :message => "Cannot be negative" }
# Set a custom error message if there is one.
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
# Check none of the object's attributes are negative.
validates_each attr_names do |obj, atr, val| obj.errors.add(atr, configuration[:message]) if val
Note: This particular validation is complementary to validates_numericality_of
, you’ll get some odd failures if you don’t use it.
We need to require
this file before we can use the new validation in our model:
class Badger 'no-one is that light'
end
The first validates_positive_or_zero
validates two attributes at once, whereas the second validates only one attribute with a custom error message.
Testing the Validation
I feel I should give a little introduction to Shoulda, it’s one of those tools that leaves me wondering how I ever managed before. Despite the popularity of RSpec within the Ruby community, I’m not a fan, it’s just too verbose, which I find gets in the way (it doesn’t render views though, which makes functional testing a lot easier, this feature for standard Rails tests please!). Shoulda is the answer for those of us who want standard Rails tests, but with them being neater and tidier. I encourage you to check it out more fully, but for this example, I’ll stay on-subject.
For our custom validation we could test it in the regular fashion with a test_foo method, or we could write a custom method that we could use again and again without re-writing the same code. The latter is the approach we are going to take here, with the method being in the style of the other shoulda validation tests.
First up, install the Shoulda plugin:
./script/plugin install git://github.com/thoughtbot/shoulda.git
Next, create the file RAILS_ROOT/test/shoulda_macros/validations.rb
with the following contents:
class Test::Unit::TestCase
def self.should_only_allow_positive_or_zero_values_for(*attributes)
configuration = { :message => "Cannot be negative" }
configuration.update(attributes.pop) if attributes.last.is_a?(Hash)
klass = model_class
attributes.each do |attribute|
attribute = attribute.to_sym
should "only allow positive or zero values for #{attribute}" do
assert_bad_value(klass, attribute, -1, configuration[:message])
end
end
end
private
# Taken from the shoulda private methods
def model_class
self.name.gsub(/Test$/, '').constantize
end
end
Now we set up our tests in RAILS_ROOT/test/units/badger_test.rb
:
require 'test_helper'
class BadgerTest 'no-one is that light'
end
And we’re done! I hope this was useful for you.