Shoulda macros are so neat and tidy, aren’t they? I love kicking off my unit tests with quick-and-deadly validation and association tests. Here’s an example:
require 'test_helper' class CouponTest < ActiveSupport::TestCase should_validate_presence_of :code, :name, :description context "validating uniqueness" do setup do Factory :coupon end should_validate_uniqueness_of :code end should_belong_to :user end
Do you see anything wrong with this picture?
should_validate_uniqueness_of necessarily requires that you already have a record in the database. As much as I hate database interaction in my tests, it’s a necessary evil here. The macro works by copying the attributes of an existing record, and validating it to see if you get any uniqueness errors. So, for this one test, I have to setup a context with a Factory call, because I’m not about to create a record for all the other tests that don’t need it.
Wouldn’t it be nice if you could call
should_validate_uniqueness_of and it would create the record for you, if one didn’t already exist? It could use the Factory you’ve already setup. And it would still work the old way if you don’t have factories, or don’t enjoy DRY coding.
Add this macro to your application:
# test/shoulda_macros/validation_macros.rb module Test module Unit class TestCase class << self alias_method :svuo_original, :should_validate_uniqueness_of def should_validate_uniqueness_of(*attributes) class_name = self.name.gsub(/Test$/, '') klass = class_name.constantize model_sym = class_name.underscore.to_sym context "with a record in the database" do setup do Factory model_sym unless klass.count > 1 end svuo_original *attributes end end end end end end
Here, we’re overriding the default Shoulda macro. Before we call the original, we’ll setup a context and create the record using our Factory, unless a record is already created. That means it will always “just work”, with the exact same syntax, and all of the options of the original
Thoughtbot can’t create the macro like this by default, because it assumes you have
factory_girl installed. But if you do use factories, this upgrade will take some of the ugly out of your tests:
require 'test_helper' class CouponTest < ActiveSupport::TestCase should_validate_presence_of :code, :name, :description should_validate_uniqueness_of :code should_belong_to :user end
Nice, eh? As usual, all you need to do to install/create a new shoulda macro is drop a ruby file into the
test/shoulda_macros folder, with macro methods defined. You don’t even need to reopen TestCase the way I did, unless you’re overriding an existing macro.