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?
[EDIT]
Maybe the most interesting part of this question is Why I cannot patch
somefunction.__call__
?Because the function don't use
__call__
's code but__call__
(a method-wrapper object) use function's code.I don't find any well sourced documentation about that, but I can prove it (Python2.7):
Replace
f
's code byg
's code:Of course
f
andf.__call__
references are not changed:Recover original implementation and copy
__call__
references instead:This don't have any effect on
f
function. Note: In Python 3 you should use__code__
instead offunc_code
.I Hope that somebody can point me to the documentation that explain this behavior.
You have a way to work around that: in
utils
you can defineAnd now your patch can work like a charm.
Follow the original answer that I consider even the best way to implement your tests.
I've my own gold rule: never patch protected methods. In this case the things are little bit smoother because protected method was introduced just for testing but I cannot see why.
The real problem here is that you cannot to patch
datetime.datetime.utcnow
directly (is C extension as you wrote in the comment above). What you can do is to patchdatetime
by wrap the standard behavior and overrideutcnow
function:Ok that is not really clear and neat but you can introduce your own function like
and now
do exactly what you need without any other layer and for every kind of import.
Another solution can be import
datetime
inutils
and to patch***.utils.datetime
; that can give you some freedom to changedatetime
reference implementation without change your tests (in this case take care to changemock_utcnow()
wraps
argument too).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:
Assigning anything to
a.__call__
is pointless.However:
TLDR;
a()
does not calla.__call__
. It callstype(a).__call__(a)
.Links
There is a good explanation of why that happens in answer to "Why
type(x).__enter__(x)
instead ofx.__enter__()
in Python standard contextlib?".This behaviour is documented in Python documentation on Special method lookup.
As commented on the question, since datetime.datetime is written in C, Mock can't replace attributes on the class (see Mocking datetime.today by Ned Batchelder). Instead you can use freezegun.
Here's an example:
As you mention, an alternative is to track each module importing
datetime
and patch them. This is in essence what freezegun does. It takes an object mockingdatetime
, iterates throughsys.modules
to find wheredatetime
has been imported and replaces every instance. I guess it's arguable whether you can do this elegantly in one function.