Shoulda Macro for a Cleaner Uniqueness Test


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 should_validate_uniqueness_of

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 &lt; 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.

Advertisements

Tags: , , , , , , , ,

3 Responses to “Shoulda Macro for a Cleaner Uniqueness Test”

  1. Wes Says:

    You should show the code snippets by default and your first snippets needs quotation marks instead of the ampersand-quot-semicolon thing.

  2. Jaime Bellmyer Says:

    And YOU should know that I was merely testing you. You passed…barely :) I’ve corrected the ampersand problem, but I’m torn about showing code by default. It just makes the page so freaking long.

  3. Justin Brown Says:

    This worked perfectly. Thanks for sharing.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: