[TIP] Mock and function namespace

Jonathan Hartley tartley at tartley.com
Thu Mar 15 13:22:19 PDT 2012


This isn't trivial. It could be hacked together, but the hack I'm 
thinking of will not work in the general case.

The module that defines a function has a reference to it, and so does 
every module that imports it from the defining module:

     defining.py:
         def funcname():
             pass

     importing.py:
         from defining import funcname


'funcname' in the 'defining' module's namespace is a different variable 
than 'funcname' in the 'importing' module's namespace, even though they 
both point to the same function object. If you want to mock out both 
variables, then you have to mock out both variables separately.

If you wanted to get clever, you could write some code to search for all 
the modules in your project, and introspect them to find all 
module-level variables that reference a particular function. Then you 
could mock out each of them in turn. However, this is likely to be a bit 
flaky.
Any unusual cases, such as imports happening inside a function, will not 
be found by this method, and so if it's critical that you patch out ALL 
references to 'funcname' no matter where they occur, this won't work in 
the general case. Perhaps you have enough control over the codebase that 
you can manually ensure that none of these corner cases actually arise 
in practice.

Use 'inspect' to do the introspection. Use something a bit like:

     import inspect

     def get_functions(module):
         '''
         Return name:fn dict of all functions and lambdas defined in 
'module'
         '''
         return dict(
             inspect.getmembers(
                 module,
                 lambda item: inspect.isfunction(item)
             )
         )

     def get_modules(package):
         '''
         Return {name:module} dict of modules in the given package
         '''
         return dict(inspect.getmembers(package, inspect.ismodule))

...plus some other part to find all the packages in the project.

And use 'mock.patch' to do the patching out of the functions. It is 
clever about restoring the original values correctly when you're done.

Overall, this sounds like more trouble than it's worth, but perhaps you 
are cleverer or more persistent than I.

Best regards,

     Jonathan


On 15/03/2012 19:45, Mathieu Drapeau wrote:
> This is exactly what I wanted to highlight, is how can I also mock 
> functions that have been previously defined?
> I wanted to skim off the code but sometimes in libs, a function can be 
> namespaced or not... and I would like to trap every possible uses of 
> such function.
>
> Is it possible?
>
> Thanks,
> Mathieu
>
> Le 15 mars 2012 15:37, Jonathan Hartley <tartley at tartley.com 
> <mailto:tartley at tartley.com>> a écrit :
>
>     On 15/03/2012 18:30, Mathieu Drapeau wrote:
>>     Hi all,
>>     I am fairly new to mock and I would like to know how to fix an
>>     issue during module imports and scope of functions.
>>     Here is a snippet that explain problem I am dealing with:
>>
>>     from mock import MagicMock
>>     import sys
>>     sys.func = lambda: 'x'
>>     from sys import func
>>     print func()
>>     # outputs 'x'
>>     sys.func = MagicMock(return_value='mocked_x')
>>     print func()
>>     # outputs 'x', it is not mocked
>>
>>     How could I fix this?
>>
>>     Thanks,
>>     Mat
>>
>>
>>     _______________________________________________ testing-in-python
>>     mailing list testing-in-python at lists.idyll.org
>>     <mailto:testing-in-python at lists.idyll.org>
>>     http://lists.idyll.org/listinfo/testing-in-python 
>
>     Hey Mathieu,
>
>     Your 7th line changes the value of sys.func (the 'func' attribute
>     on local variable 'sys') but you haven't changed the value of
>     local variable 'func'. Modifying one won't affect the other. You'd
>     see the same behaviour even if MagicMock wasn't involved (e.g
>     assigning 'sys.func' to 0 will not affect the value of 'func'
>     either.)
>
>     From the code you've posted, it's not clear to me what you're
>     trying to achieve.
>
>     Perhaps you meant to write:
>
>     >>> func = Mock(return_value='mocked')
>     >>> func()
>     mocked
>
>     or
>
>     >>> sys.func = Mock(return_value='mocked')
>     >>> sys.func()
>     mocked
>
>     Best regards,
>
>         Jonathan
>
>     -- 
>     Jonathan Hartleytartley at tartley.com  <mailto:tartley at tartley.com>     http://tartley.com
>     Made of meat.+44 7737 062 225  <tel:%2B44%207737%20062%20225>        twitter/skype: tartley
>
>
>



-- Jonathan Hartley tartley at tartley.com http://tartley.com Made of meat. 
+44 7737 062 225 twitter/skype: tartley
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.idyll.org/pipermail/testing-in-python/attachments/20120315/886e17cf/attachment.htm>


More information about the testing-in-python mailing list