[mocha-developer] Automatically mocking ActiveRecord methods

James Moore jamesthepiper at gmail.com
Wed Apr 18 14:33:46 EDT 2007

On 4/2/07, Frederick Cheung <fred at 82ask.com> wrote:
> On 2 Apr 2007, at 17:24, James Moore wrote:
> >
> > I'm now wondering whether a better way to do this is to add a
> > facility to
> > add callbacks that can create mocks and stubs when a particular
> > object is
> > instantiated from the database.  A sort of cleanup method on Rails
> > find*()
> > methods that will allow you to do mock/stub methods on an object
> > with a
> > particular id (or some set of criteria) when it's created.
> I did this via the after_find callback a few weeks ago. I posted my
> code here when I did it maybe you'll find some inspiration there ?
> Fred

Thanks Fred - I took a look, and here's what I'm currently playing with.

First, I tried doing just a mock of the
The problem there was that Rails doesn't  always just do a find with an id;
when you've got  relations and validations, you'll get finds that specify
more than just the id itself.

What does get called every time, with the things you want arranged in a nice
convenient package, is #instantiate.  It's the method that turns the data
for the row into an object, and it's primary purpose in life is to support
STI.  So I hijacked it, and now am using something that looks like this:

Annotation.send :include, MockActiveRecord
foo = Foo.new :whatever => 'blah'
Foo.mock_active_record(foo.id => foo)

Every time a record of class Foo is created, the id is checked to see if it
matches an id passed to mock_active_record().  If there's a match,
that object is returned, otherwise the normal #instantiate is called to
build a new object.

Here's the current (experimental) code:

# Adds mock_active_record to an ActiveRecord class.
# Any time an ActiveRecord object is instantiated,
# its id will be checked against a list of mocked
# ids.  If the new object is present in that list,
# use the mock instead of the object produced
# by ActiveRecord.
# Example:
#   class FooTest < Test::Unit::TestCase
#     def test_foo
#       Foo.send :include, MockActiveRecord
#       # Create a new Foo in the database
#       f = Foo.create :msg => 'blah'
#       # Add a stub that will be used
#       # in place of the Foo that was just
#       # created.
#       Foo.mock_active_record(f.id => stub(:msg => :abc))
#       # find() will match the stub instead
#       # of the record from the database.
#       f = Foo.find(f.id)
#       assert_equal :abc, f.msg
#     end
#   end

module InterceptMethod
  def self.included target
    target.extend ClassMethods

  module ClassMethods
    def intercept_method call_name, &block
      unbound_method = instance_method call_name
      define_method call_name do |*args|
        bound_method = unbound_method.bind(self)
        block.call(bound_method, *args)

    def intercept_class_method call_name, &block
      class_method = method call_name
      s = class << self; self; end
      s.send :define_method, call_name do |*args|
        block.call(class_method, *args)

module MockActiveRecord
  # Classes that include MockActiveRecord
  # get:
  # - an overridden instantiate()
  # - mock_active_record()
  def self.included target
    target.class_eval do
      include InterceptMethod
      extend ClassMethods

      intercept_class_method :instantiate do |method_obj, *args|
        mocked_record_match?(*args) || method_obj.call(*args)

  module ClassMethods
    def mock_active_record ids_to_mocks
      ids_to_mocks.each_pair do |k, v|
        mocked_record_store k, v


    def mocked_record_match? record
      @@mocked_records ||= {}
      return @@mocked_records[record['id'].to_i]

    def mocked_record_store k, v
      @@mocked_records ||= {}
      @@mocked_records[k.to_i] = v

More information about the mocha-developer mailing list