DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

ActiveRecord Unittest Test Helper

08.10.2005
| 6946 views |
  • submit to reddit
        
# A mixin for Test::Unit classes to ease AR testing
#
# Use in your test class by adding
#
#   include ARTestHelper
#
# Written by Nicholas Seckar aka Ulysses
module ARTestHelper
  module InstanceMethods
    # Access the temporary instance to test.
    # In my setup method, I store a new, valid instance in an instance variable such as @p.
    #
    # The ivar is the first character of the class (@p for Page, @u for User), and usually has an
    # attr_reader for it.
    #
    # This method should return that instance so that the validation checker can access it.
    def instance
      instance_variable_get("@#{self.class.name[0, 1].downcase}")
    end
  end
  
  module ClassMethods
    # Test that the named associations can be loaded
    # Catches errors such as missing tables, classes, etc.
    # Only use one directive per test-case
    #
    # Example: check_associations %w(friends downloads links)
    def check_associations(*names)
      define_method "test_associations" do
        assert(instance.save, 'Must be able to save instance!') if instance.new_record?
        names.flatten.each do |assoc|
          v = instance.send assoc
          v.each { } if v.respond_to?(:each) # Load collections by using each
        end
      end
    end

    # Check the validations for the named field
    # 
    # Example:
    #
    # check_validations_for :email do
    #   invalid nil, "can't be blank"
    #   invalid 'boo', /not .* valid .* email/
    #   valid 'fakeuser@host.com'
    # end
    def check_validations_for(field, &checks)
      define_method("test_validations_for_#{field}") do
        ValidationChecker.new(field, checks, self, instance).run
      end
    end
  end
  
  class ValidationChecker < Struct.new(:field_name, :proc, :testcase, :instance)
    # Assign the given value to the instance
    def assign(value)
      instance.send "#{field_name}=", value
    end

    # Ensure that this value is invalid.
    # If a message is supplied, it will be matched against the validation error message.
    # The message is matched using === so you can use Regexp's.
    def invalid(value, message = nil)
      assign value
      assert ! instance.valid?, "expected #{field_name} = #{value.inspect} to be invalid"
  
      return unless message
      
      found_msg = instance.errors.on(field_name)
      assert message === found_msg, "\nexpected error message: #{message.inspect}\nfound: #{found_msg.inspect}"
    end

    # Ensure that the given value is valid.
    def valid(value)
      assign value
      assert instance.valid?, "expected #{field_name} = #{value.inspect} to be valid"
      assert instance.save # Make sure the database agree's with the validations
    end

    # Run the validation tests
    def run
      assert(instance.valid?, "Initial object ought to be valid!")
      instance_eval(&proc)
    end
  
    undef_method :assert # Use the testcase's method
  
    # Forward missing methods to the testcase if possible
    def method_missing(sel, *args)
      testcase.respond_to?(sel) ? testcase.send(sel, *args) : super(sel, *args)
    end
  end

  def self.append_features(cls)
    cls.send :include, InstanceMethods
    class << cls
      include ClassMethods
    end
  end
end