Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Patch __call__ of a function

Tags:

I need to patch current datetime in tests. I am using this solution:

def _utcnow():     return datetime.datetime.utcnow()   def utcnow():     """A proxy which can be patched in tests.     """     # another level of indirection, because some modules import utcnow     return _utcnow() 

Then in my tests I do something like:

    with mock.patch('***.utils._utcnow', return_value=***):         ... 

But today an idea came to me, that I could make the implementation simpler by patching __call__ of function utcnow instead of having an additional _utcnow.

This does not work for me:

    from ***.utils import utcnow     with mock.patch.object(utcnow, '__call__', return_value=***):         ... 

How to do this elegantly?

like image 734
warvariuc Avatar asked Dec 14 '15 06:12

warvariuc


People also ask

What is Side_effect in mock python?

side_effect: A function to be called whenever the Mock is called. See the side_effect attribute. Useful for raising exceptions or dynamically changing return values. The function is called with the same arguments as the mock, and unless it returns DEFAULT , the return value of this function is used as the return value.

What is Patch in Python?

patch() uses this parameter to pass the mocked object into your test. From there, you can modify the mock or make assertions as necessary. You can execute this test module to ensure it's working as expected: $ python tests.py . -------------------------------------------------------

What is MagicMock Python?

MagicMock. MagicMock objects provide a simple mocking interface that allows you to set the return value or other behavior of the function or object creation call that you patched. This allows you to fully define the behavior of the call and avoid creating real objects, which can be onerous.


1 Answers

When you patch __call__ of a function, you are setting the __call__ attribute of that instance. Python actually calls the __call__ method defined on the class.

For example:

>>> class A(object): ...     def __call__(self): ...         print 'a' ... >>> a = A() >>> a() a >>> def b(): print 'b' ... >>> b() b >>> a.__call__ = b >>> a() a >>> a.__call__ = b.__call__ >>> a() a 

Assigning anything to a.__call__ is pointless.

However:

>>> A.__call__ = b.__call__ >>> a() b 

TLDR;

a() does not call a.__call__. It calls type(a).__call__(a).

Links

There is a good explanation of why that happens in answer to "Why type(x).__enter__(x) instead of x.__enter__() in Python standard contextlib?".

This behaviour is documented in Python documentation on Special method lookup.

like image 163
zvone Avatar answered Sep 27 '22 20:09

zvone