[TIP] Problem mocking a decorator

Aviv Ben-Yosef aviv.by at gmail.com
Tue Dec 28 07:47:48 PST 2010


Hey Yoni,

On Tue, Dec 28, 2010 at 17:32, Yoni Tsafir <yonix85 at gmail.com> wrote:

> Hi guys,
> I'm pretty new to the world of testing in python, and there's a problem I
> ran into while trying to mock a decorator in the code I'm testing.
> Tell me what am I doing wrong or if this is a missing feature in the mock
> library.
>
> I have the following code:
>
> a.py:
> -------
> import sys
>
> class BadArgsError(Exception):
>     pass
>
>
> def catch_bad_args(func):
>     def call(*args, **kwargs):
>         try:
>             return func(*args, **kwargs)
>         except BadArgsError, e:
>             print >> sys.stderr, e.args[0]
>             print
>             sys.exit(1)
>
>     call.__name__ = func.__name__
>     call.__dict__ = func.__dict__
>     call.__doc__  = func.__doc__
>     return call
>
> b.py
> ------
> from a import BadArgsError, catch_bad_args
>
> @catch_bad_args
> def foo(arg):
>     if arg > 2:
>         raise BadArgsError("arg is bigger than 2")
>
>     print "arg is ok"
>
> test_b.py
> -------------
> import unittest
> from b import foo
> from a import BadArgsError
> from mock import patch
>
> def do_nothing_decorator(func):
>     return func
>
>
> class BTest(unittest.TestCase):
>         @patch("a.catch_bad_args", do_nothing_decorator)
>         def test_three_raises(self):
>                 self.assertRaises(BadArgsError, foo, 3)
>
>
> Now running the tests:
> -------------------------------
> # python test_b.py
> arg is bigger than 2
>
> E
> ======================================================================
> ERROR: test_three_raises (__main__.BTest)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
>   File "test_b.py", line 13, in test_three_raises
>     self.assertRaises(BadArgsError, foo, 3)
>   File "/usr/lib/python2.6/unittest.py", line 336, in failUnlessRaises
>     callableObj(*args, **kwargs)
>   File "/tmp/a.py", line 14, in call
>     sys.exit(1)
> SystemExit: 1
>
> ----------------------------------------------------------------------
> Ran 1 test in 0.014s
>
> FAILED (errors=1)
>
>
> What am I doing wrong? I tried to change the patch to "b.catch_bad_args"
> and I get the same results, I also tried @patch.object and other
> combinations... No help
>
>
AFAICT, the problem is that once you import the file the function is
evaluated and the decorator is applied to it, before you patch it.
I think that instead of messing with patching decorators, you can make your
decorator export the wrapped function, for example by setting an attribute
.decorated on the new function so you'd be able to access it freely in your
tests.

BTW, instead of copying yourself the function attributes, it is advised to
use functools.wraps

Hope this helps,
Aviv


>
> Thanks a lot!
>
> _______________________________________________
> testing-in-python mailing list
> testing-in-python at lists.idyll.org
> http://lists.idyll.org/listinfo/testing-in-python
>
>


-- 
Aviv Ben-Yosef
Miracle Max: You rush a miracle man, you get rotten miracles.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.idyll.org/pipermail/testing-in-python/attachments/20101228/4e1e5582/attachment.htm>


More information about the testing-in-python mailing list