war*_*iuc 21 python time mocking python-mock
我需要在测试中修补当前的日期时间.我正在使用这个解决方案:
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()
Run Code Online (Sandbox Code Playgroud)
然后在我的测试中我做了类似的事情:
with mock.patch('***.utils._utcnow', return_value=***):
...
Run Code Online (Sandbox Code Playgroud)
但今天我想到了一个想法,我可以通过修补__call__功能utcnow而不是额外的功能来简化实现_utcnow.
这对我不起作用:
from ***.utils import utcnow
with mock.patch.object(utcnow, '__call__', return_value=***):
...
Run Code Online (Sandbox Code Playgroud)
如何优雅地做到这一点?
Mic*_*ico 13
[编辑]
也许这个问题最有趣的部分是为什么我无法修补somefunction.__call__?
因为函数不使用__call__代码而是__call__(方法包装器对象)使用函数的代码.
我没有找到任何有关这方面的资料来源,但我可以证明它(Python2.7):
>>> def f():
... return "f"
...
>>> def g():
... return "g"
...
>>> f
<function f at 0x7f1576381848>
>>> f.__call__
<method-wrapper '__call__' of function object at 0x7f1576381848>
>>> g
<function g at 0x7f15763817d0>
>>> g.__call__
<method-wrapper '__call__' of function object at 0x7f15763817d0>
Run Code Online (Sandbox Code Playgroud)
用f代码替换g代码:
>>> f.func_code = g.func_code
>>> f()
'g'
>>> f.__call__()
'g'
Run Code Online (Sandbox Code Playgroud)
当然f,f.__call__参考文献没有改变:
>>> f
<function f at 0x7f1576381848>
>>> f.__call__
<method-wrapper '__call__' of function object at 0x7f1576381848>
Run Code Online (Sandbox Code Playgroud)
__call__改为恢复原始实现和复制引用:
>>> def f():
... return "f"
...
>>> f()
'f'
>>> f.__call__ = g.__call__
>>> f()
'f'
>>> f.__call__()
'g'
Run Code Online (Sandbox Code Playgroud)
这对f功能没有任何影响.注意:在Python 3中,您应该使用__code__而不是func_code.
我希望有人能指出我解释这种行为的文档.
你有办法解决这个问题:utils你可以定义
class Utcnow(object):
def __call__(self):
return datetime.datetime.utcnow()
utcnow = Utcnow()
Run Code Online (Sandbox Code Playgroud)
现在你的补丁可以像魅力一样工作.
按照我认为是实现测试的最佳方式的原始答案.
我有自己的黄金法则:从不修补受保护的方法.在这种情况下,事情有点平滑,因为保护方法只是为了测试而引入,但我不明白为什么.
这里真正的问题是你不能datetime.datetime.utcnow直接修补(正如你在上面的评论中写的那样是C扩展).你可以做的是datetime通过包装标准行为和覆盖utcnow功能来修补:
>>> with mock.patch("datetime.datetime", mock.Mock(wraps=datetime.datetime, utcnow=mock.Mock(return_value=3))):
... print(datetime.datetime.utcnow())
...
3
Run Code Online (Sandbox Code Playgroud)
好吧,这不是很清楚,但你可以介绍自己的功能,如
def mock_utcnow(return_value):
return mock.Mock(wraps=datetime.datetime,
utcnow=mock.Mock(return_value=return_value)):
Run Code Online (Sandbox Code Playgroud)
现在
mock.patch("datetime.datetime", mock_utcnow(***))
Run Code Online (Sandbox Code Playgroud)
在没有任何其他图层的情况下完成所需的操作,并进行各种导入.
另一种解决方案可以是进口datetime中utils和补丁***.utils.datetime; 这可以让你有一些自由来改变datetime参考实现,而无需改变你的测试(在这种情况下也要注意改变mock_utcnow() wraps参数).
zvo*_*one 13
修补__call__函数时,您正在设置该实例的__call__属性.Python实际上调用了类上定义的方法.__call__
例如:
>>> 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
Run Code Online (Sandbox Code Playgroud)
分配任何东西a.__call__都是毫无意义的.
然而:
>>> A.__call__ = b.__call__
>>> a()
b
Run Code Online (Sandbox Code Playgroud)
a()不打电话a.__call__.它叫type(a).__call__(a).
有一个很好的解释为什么会出现这种情况来回答"为什么type(x).__enter__(x)而不是x.__enter__()Python标准contextlib?" .
有关特殊方法查找的 Python文档中记录了此行为.