A case for methodizing your constants

I was testing a bit of Rails code today and came across a spot where I wanted a unit test to make sure my method was going to properly handle an upper limit on associated objects. Think of it like this:

class Release < ActiveRecord::Base
  has_many :tags
  
  MAX_TAGS = 15
  
  def associate(tag)
    self.tags << tag unless self.tags.size == MAX_TAGS
  end
end

At 15 max tags, either I'm going to clutter up my tests with dummy data, or I'm going to drive myself insane creating needless fixtures. I use Mocha to stub out methods in my tests, so I immediately asked myself, "How can I stub a constant?" You can go ahead and alter the constant in your test case using const_set, but that requires that you set it back at the end of your test. Another option is to methodize the constant and stub it out like you would any other method:

class Release < ActiveRecord::Base
  has_many :tags
  
  MAX_TAGS = 15
  
  def self.max_tags
    MAX_TAGS
  end
  
  def associate(tag)
    self.tags << tag unless self.tags.size == Release.max_tags
  end
end

Now, in your test case, you can just stub out your new class method and, provided you're now accessing the constant in your original method through its new wrapper method, you're all set:

class ReleaseTest < ActiveSupport::TestCase
  def test_associate
    Release.stubs(:max_tags).returns(1)
    
    assert_no_difference 'release(:first).tags.reload.size' do
      release(:first).associate(tags(:new))
    end
  end
end

Leave a Reply