[TIP] ANN: Fudge 1.0 - YAMF (yet another mock framework)
fuzzyman at voidspace.org.uk
Sat Feb 26 10:00:40 PST 2011
On 25/02/2011 22:19, Kumar McMillan wrote:
> Everyone else is doing it, right?! Mock testing tools abound!
> Fudge has come along way since its 0.9.0 release in late 2009. As the
> flexmock docs point out, one of its major flaws in 0.9 was that you
> had to manage setup/teardown so that expectations got verified. Since
> 1.0 that is no longer the case. The docs above explain how it works
> but as a spoiler: I basically stole mock's @patch method. I always
> liked the way @patch worked but never thought it would add much value
> to Fudge. Then one day, while working on a mock based suite , it
> dawned on me that this would be a perfect way to seamlessly inject
> fudge's verification step. Unlike flexmock it requires a decorator
> but also unlike flexmock it doesn't couple you to your test runner.
Thanks for the nice things you say about mock. Good to see that mock is
influencing other testing tools in the Python world.
Forgive me if I'm wrong (which is quite likely), but from a naive
reading of the implementation of patch in fudge it looks like there are
quite a few situations it gets 'wrong'. (Monkey patching is actually
tricky which is exactly why its useful to have tools that do it for you.)
As far as I can tell fudge.patch is doing a getattr / setattr pair to
get the original attribute and then restore when undoing the patch. It
also has some special casing for handling cases where setting the new
attribute (the mock) fails.
If I'm right about how the original attribute is restored  then it
will do the wrong thing (some more serious than others) under the
* If you are patching a method on an instance then getattr(obj,
attr_name) will return a bound method. Actually deleting the object you
set on the instance is enough to restore the object to its original
state - setting the bound method as an instance attribute is technically
incorrect but probably *generally* not a problem.
* If you are patching a class attribute defined on a base class then
getattr(klass, attr_name) returns the attribute from the base class.
Resetting that back on the class when the patching is complete is again
incorrect (merely deleting the monkeypatched attribute is the correct
behavious). In this case you are permanently modifying the class by
setting an attribute from a base class onto it. This is probably bad.
* If the attribute is a descriptor like a static method then
getattr(klass, attr_name) returns an unbound function and not the
descriptor. When you set that attribute back on the class it becomes a
method! The correct behaviour is to fetch and restore the original from
__dict__ rather than using getattr.
You are very welcome to re-use the logic that mock.patch uses to get
these situations correct. See _path.get_original and _patch.__exit__.
The basic logic for fetching the original is to first check if the name
exists in obj.__dict__ (handling the case where there is no __dict__).
If this succeeds then you're doing a local patch and you restore the
original by putting it back in obj.__dict__.
If it isn't local then you use getattr to fetch the original.
For restoring a non-local patch you can usually restore the original
state with a delattr, unless there was no __dict__ in which case you
must use setattr. The exception here is certain kinds of proxy objects
(like django settings) which proxy attribute access. To avoid this after
deleting the patched attribute I then check (with hasattr) to ensure the
attribute is now available. If not then I do a setattr.
Hope this is helpful,
>  Mock is by far the most pervasive mock testing tool. People love
> it so much that I often lose the vote when trying to introduce Fudge
> into a new project :) I like mock too but I really don't like
> postmortem inspection and wanted more direct tracebacks.
> testing-in-python mailing list
> testing-in-python at lists.idyll.org
May you do good and not evil
May you find forgiveness for yourself and forgive others
May you share freely, never taking more than you give.
-- the sqlite blessing http://www.sqlite.org/different.html
More information about the testing-in-python