Today I came across a case where I wanted to temporarily redefine a method’s implementation for purposes of testing. Specifically I want to test with a condition of a large number of records in a database. Since I don’t actually want to load a large number of records into the database I just want to make the system think there is a large number of records for the purposes of the test.
Here’s a gist showing what I ended up doing:
module Count def fake_count words.count + 500 endend
class Thing def words %w(one two three) end def count words.count endend
def assert(value) raise "Assertion failed" unless value == trueend
thing = Thing.newassert thing.count == 3puts thing.count
class Thing include Count alias_method :real_count, :count alias_method :count, :fake_countend
thing = Thing.newassert thing.count == 503puts thing.count
class Thing alias_method :count, :real_countend
thing = Thing.newassert thing.count == 3puts thing.count- Create a module that has the faked version of the method
- Open up the original class and include the module (I want it on all instances – if it were just one I’d use extend)
- While the class is open call alias_method :real_count, :count which creates an alias of real_count that points to the count method
- While still in the open class, call alias_method :count, :fake_count which creates an alias of count to the fake_count method. Note that real_count still points to the original implementation
Once the class has been opened and modified it will return the new value. To undo the change I can just open up the class again and call alias_method :count, :real_count which makes count behave as it did originally.
Why not stub? In this case I wanted to use a method in Thing and just add 500 to it. I probably could have also called real_count + 500 which would have effectively decorated the original count method.
Can you think of alternative approaches? Let me know in the comments.

#1 by Chris Morris on July 19, 2011 - 8:57 am
Another option: you could subclass Thing with FakeLargeThing, and just use FakeLargeThing in the one test. This way you wouldn’t have to modify Thing at all or worry about restoring the aliased methods.
class Thing
def words
%w(one two three)
end
def count
words.count
end
end
class FakeLargeThing < Thing
def count
super + 500
end
end
def assert(value)
raise "Assertion failed" unless value == true
end
thing = Thing.new
assert thing.count == 3
puts thing.count
thing = FakeLargeThing.new
assert thing.count == 503
puts thing.count
thing = Thing.new
assert thing.count == 3
puts thing.count