Let's build a Ruby mocking framework! Sorta...kinda?
January 27, 2012
For whatever reason, I was thinking about mocking and stubbing the other day.
It’s pretty cool that testing librarys allow such fine-grain control of objects under test. It’s powerful to be able to write tests with functionality like:
I got to thinking about how you would actually implement such a library. I wanted to explore some concepts of metaprogramming - but I didn’t want to see what something like NUnit or RSpec did under the hood. That would be cheating; I wanted to see if I could figure out an approach that might work on my own.
I sat down to try out some ideas and quickly discovered that trying to duplicate all the functionality of a mocking/stubbing framework is hard work. Certainly harder than I wanted to spend on a toy exercise on a Friday evening!
So I settled on trying to implement a basic Stub.
- Make a class StubbedFoo that derives from Foo
- Intercept all methods called on StubbedFoo before they went to Foo
- Return a canned response based on the method name
- Allow method/response pairs to be added to StubbedFoo
Here’s the mostly-working toy implementation in Ruby (took me about 90 minutes):
I used Ruby’s
undef to undefine the instance methods of the class
under test on-the-fly. I then build up a
@canned_responses hash that uses
the method name (as a symbol) for the key.
When you try to call a method (like
Statistics#compute_average) it hits
method_missing (since that method is now Undefined). The stub
simply returns the canned value from the hash or raises an exception if there
is no response setup.
I tried to figure out how to structure the code so I could just mix it into a class, instead of deriving directly - but I couldn’t get it working. Any tips would be appreciated!
I’ve only scratched the surface of what you can do with metaprogramming but I think this was a worthwhile exercise. I learned some new Ruby tricks and challenged myself to think about how code I take for granted is actually implemented.