"What happens though, if you want to override a class in an instance of an object, and not all of its kind? Typically you would define a mock object, and create an instance of it. But, in Ruby there is an easier and faster way that doesn’t involve writing a different mock class for each different scenario – and it is made possible by the singleton class. This clever bit of ruby hackery lets you override the behaviour of a single instance of a class, creating what I’ve decided to call a partial mock object. To demonstrate, I’ve written a small method called override_method which will override the behaviour of the specified method in the passed object, like so:
# Overrides the method +method_name+ in +obj+ with the passed block def override_method(obj, method_name, &block) # Get the singleton class/eigenclass for 'obj' klass = class <<obj; self; end # Undefine the old method (using 'send' since 'undef_method' is protected) klass.send(:undef_method, method_name) # Create the new method klass.send(:define_method, method_name, block) end # Just an example class class Foo def do_stuff "I'm okay!" end end # Test code list = [] 5.times { list.push(Foo.new) } # We override the method here! override_method(list.first, :do_stuff) { "I'm NOT okay!" } list.each_with_index { |f, i| puts "(#{i}) #{f.do_stuff}" } Outputs: (0) I'm NOT okay! (1) I'm okay! (2) I'm okay! (3) I'm okay! (4) I'm okay!
As you can see, only the first object in the array’s behaviour has been changed – the rest have remained untouched. Because of this, you can embed these partial dynamic mock objects deeply into your code without the need to specially instantiate a mock object deep in your code, or writing a ‘clever mock’ to only trigger the determined behaviour in certain objects.
Where this code comes in really handy is when you need an object to raise a difficult to simulate exception (like a disk full error) on a certain method to test your error handling – simply call override_method and pass in a call to raise and voila! Dynamic partial mock objects on the fly!"
override_method(list.first, :do_stuff) { "I'm NOT okay!" }
... you would write ...
list.first.stubs(:do_stuff).returns("I'm NOT okay!")
Where Mocha really comes into its own is doing this for classes e.g.
Foo.stubs(:create).returns(mock_foo)
In this case Mocha puts the class back together at the end of each test method, so other tests will be unaffected.