Update (9/5/07): My project has since switched over to UnitRecord for the reasons outlined by Dan Manges.
Along with many others, I believe that unit tests should have as few dependencies as possible and be very fast. In rails, this means disconnecting models from the database when running unit tests.
My project uses the following solution, which is derived from the solution posted at Jay’s blog: Rails: ActiveRecord Unit Testing part II
The difference is that this solution does not unbind and rebind the initialize method. Instead, it uses the inherited hook to include a module with the necessary code on each ActiveRecord::Base subclass. My team and I like this solution better since it doesn’t unbind and redefine methods, but rather uses ruby’s object model to insert code in the call hierarchy between the model and ActiveRecord::Base.
The full solution belongs in a file (unit_test_helper.rb) that is required by each unit test:
module DisconnectedModel def initialize(attributes={}) self.class.stubs(:columns).returns(no_db_columns(attributes)) super end def no_db_columns attributes return [] if attributes.nil? attributes.keys.collect do |attribute| sql_type = case attributes[attribute].class when " " then "integer" when "Float" then "float" when "Time" then "time" when "Date" then "date" when "String" then "string" when "Object" then "boolean" end ActiveRecord::ConnectionAdapters::Column.new(attribute.to_s, nil, sql_type, false) end end end class ActiveRecord::Base def self.inherited(cls) cls.class_eval do include DisconnectedModel end end end class << ActiveRecord::Base def connection raise 'You cannot access the database from a unit test' end end
Models can be tested by passing in the columns and their values into the new method:
require File.dirname(__FILE__) + '/../unit_test_helper' class DogTest < Test::Unit::TestCase def test_name dog = Dog.new(:name => "fido") assert_equal "fido", dog.name end end
Models can even have their own initialize method as long as it calls super:
def test_translate_age_into_dog_years dog = Dog.new(:age => 3) assert_equal 21, dog.age end class Dog < ActiveRecord::Base def initialize(args={}) human_age = args[:age] || 0 super(args.merge(:age => human_age * 7)) end end
One Response to “Another solution for disconnected unit tests”
Sorry, the comment form is closed at this time.




I think this approach has a few problems. I “blogged about the problems”:http://www.dcmanges.com/blog/rails-unit-record-test-without-the-database and created a gem called “UnitRecord”:http://www.dcmanges.com/blog/rails-unit-record-test-without-the-database that defines columns based on schema.rb