module InactiveRecord module Finders # Ease the task of mocking ActiveRecord finder methods. # # Ex.: # # Product.finds 1, :name => 'Pizza' # # Then calling Product.find(1) will return an instance of product # with the id 1 and the name Pizza. # Product.find(:all) will return all records registered with the +finds+ # method. You also get the Product.count for free. # # You can also use the wildcard magic word +anything+ to return the # same thing whatever the id or the finder used. # # Product.finds :anything, :name => 'god' # # Product.find_by_name 'what?' # => <#Product name=god> # Product.find(123) # => <#Product name=god> # # finds_by_* allows you to use the find_by_* methods. # Additionnaly the corresponding method will be initialized. # # Product.finds_by_name 'marc' # # Product.find_by_name 'marc' # => <#Product name=marc> # def finds(record_id, attributes={}) self.inactivate! attributes record = self.new(attributes) if record_id == :anything record.stubs(:id).returns 1 self.stubs(:find).returns record self.stubs(:find_every).returns [record] else record.stubs(:id).returns record_id self.stubs(:find).with{ |id, *args| id == record_id || id == record_id.to_s }.returns record record.extend InstanceMethods end reset_global_cache end def finds_by(attribute_name, attribute_value, attributes={}) attributes ||= {} self.inactivate! attributes record = self.new(attributes) record.stubs(:id).returns attributes[:id] || 1 record[attribute_name] = attribute_value self.stubs(:"find_by_#{attribute_name}").returns nil self.stubs(:"find_by_#{attribute_name}").with(attribute_value).returns record end def method_missing_with_finds(method, *args) if matches = method.to_s.match(/^finds_by_(.*)$/) finds_by(matches[1], args[0], args[1]) else method_missing_without_finds method, args end end alias_method_chain :method_missing, :finds def destroy_mock(id) remove_expectations_for(:find, id) reset_global_cache true end # Module mixed-in all stubed records module InstanceMethods def destroy self.class.destroy_mock(id) end end private def reset_global_cache # Finds all previous expectation for find(id) expectations = expectations_for(:find, instance_of(Fixnum)) self.stubs(:count).returns expectations.size self.stubs(:find).with(:all).returns expectations.collect { |exp| exp.invoke } end def expectations @mocha.expectations.instance_eval{@expectations} end def expectations_for(method, *parameters) expectations.find_all do |exp| exp.match?(method, *parameters) end end def remove_expectations_for(method, *parameters) expectations.reject! do |exp| exp.match?(method, *parameters) end end end end