shoulda == 'painless unit testing'
October 15th, 2008
I’m just getting started unit testing my models with shoulda and so far it has been a wonderful experience. In large part, shoulda is no more than syntactic sugar for Test::Unit::TestCase. However, the crux of the philosophy behind shoulda is that it ought to be just as easy to test your ActiveRecord models as it is to write them. So, if it only requires one line of code in your model to build an ActiveRecord association, then it should only take one line of code to test that association. Such is the magic of shoulda.
Equally important is the simplicity of the core parts of the library. The shoulda source is extremely easy to read and follow. For example. the ActiveRecord macros are simply methods that wrapshould blocks, which are mixed in to Test::Unit::TestCase as class methods.
# taken from ThoughtBot::Shoulda::ActiveRecord::Macros
def should_require_attributes(*attributes)
message = get_options!(attributes, :message)
message ||= default_error_message(:blank)
klass = model_class
attributes.each do |attribute|
should "require #{attribute} to be set" do
assert_bad_value(klass, attribute, nil, message)
end
end
end
It wasn’t long before I found myself wanting a macro to test if a model contained a specific callback. It’s so easy to create your own macros it’s silly. Simply create a folder called “shoulda_macros” in RAILS_ROOT/test/ and any files in that folder are automatically included. Here’s the macro I wrote:
class Test::Unit::TestCase
def self.should_have_callback(type, name)
klass = model_class
should "have #{type.to_sym} callback named #{name.to_sym}" do
assert_not_nil(klass.send(type.to_sym).detect { |callback| callback.kind == type.to_sym && callback.method == name.to_sym }, "#{type.to_sym} callback method #{name.to_sym} not found.")
end
end
end
In my opinion, shoulda is hands down the most painless way to test ActiveRecord models. Also, the fact that it is not a drop-in replcement for Test::Unit, rather, it builds on top of it, is a major plus for me. Call me conservative, but there is a certain added feeling of safety I have when my test framework is built on top of the built-in Ruby testing package (Test::Unit), rather than replacing it outright, which is what rspec does. (I am still using rspec to test my controllers, and I’m looking forward to seeing what shoulda offers in this department)
For a great introduction and explanation of shoulda, checkout Tammer Saleh’s presentation from MountainWest RubyConf 2008.
Leave a Reply