redding/assert — pull request #199 by jcredding (2014-07-03 16:28:15+00:00)
Title: Handle stubbing a method on a parent and child in the same test

This fixes a rare bug with stubbing a method on a parent class
and on a child class that inherits from the parent class in the
same test. This caused order-dependent bugs when the stubs were
tore down. If the parent was stubbed and tore down before the
child, then the child tear down would fail because the parent
would have already removed the method it wanted to remove. This
fixes the issue by taking steps to ensure the parent and child
don't share stub methods:

* The stub method is now namespaced by the object it is intended
for. This ensures the parent and child do not use the same stub
methods.
* The conditions for detecting if a proxy method needs to be
defined has been expanded. They now detect if the object is a
module/class or not. If the object is a module or class, then it
needs to define a proxy method if the method is not directly
defined on the class. So if we are stubbing an inherited class
method, then it needs to define a proxy method. If the object
isn't a module or class, then we only want to define a proxy
method if it doesn't define the method itself. This is primarily
for stubbing an instance that uses `method_missing`. This ensures
we don't alias an inherited method, which will alter the object
the method is inherited from, but leaves all the existing features
working.
* The condition for detecting if the method has already been
stubbed is more restrictive. Now it will only look at local
methods on the object. This ensures we don't think a method has
already been stubbed because the method on a parent class has
been stubbed.
* To avoid arity errors, the stub method is now defined via
`class_eval` and a string instead of `define_method`. This ensures
the stub method is not created with an arity of `1` which causes
arity checks to fail. This properly creates a method with an arity
of `-1`.
* To support the `class_eval`, the stub instance needs to be
stored on the object being stubbed. When the stub method is called,
it will look up the stub instance and call it like it previously
did. This sets and removes an ivar in the setup and teardown to
support this.

All of this fixes issues with stubbing inherited methods in the
same test. The typical use-case for this is stubbing `new` on
a parent class and a child class. Since all classes have `new`
this was the most common scenario for this error to show up.

@kellyredding - Ready for review. I tried to explain all the changes and why they were needed. Sorry for the lengthy commit message. I haven't added any system tests and didn't want to hold this up while I worked out what all scenarios I wanted to test (there are more scenarios, previously I hadn't considered the `method_missing` cases until it started failing on me in the test suite). I have a regression test to ensure this specific error doesn't come back.