Python 使用mock.patch.object在类的所有对象上包装方法
在一些asyncio测试案例中,我发现我经常希望等待调用某个方法,因此我编写了以下上下文管理器:Python 使用mock.patch.object在类的所有对象上包装方法,python,python-3.x,python-unittest,Python,Python 3.x,Python Unittest,在一些asyncio测试案例中,我发现我经常希望等待调用某个方法,因此我编写了以下上下文管理器: @contextmanager def wait_for_call(loop, obj, method, calls = 1): # set up some bookkeeping including a future called fut def cb(*args, **kwards): # Set fut's result to true if the right
@contextmanager
def wait_for_call(loop, obj, method, calls = 1):
# set up some bookkeeping including a future called fut
def cb(*args, **kwards):
# Set fut's result to true if the right number of calls happen
try:
with mock.patch.object(obj, method,
wraps = getattr(obj, method),
side_effect = cb):
yield
loop.run_until_complete(asyncio.wait_for(fut, 0.5))
except asyncio.futures.TimeoutError:
raise AssertionError("Timeout waiting for call to {} of {}".format(
method, obj)) from None
如果我正在修补一个特定的实例或者修补一个类方法,那么这种方法非常有效。但是,在一些情况下,我希望像这样修补常规(实例)方法:
class foo:
def bar(self): pass
x = foo()
with wait_for_call(loop, x, 'bar'): x.bar()
当我这样做时,我会得到一个TypeError
,因为x.bar
不会得到self
。我认为这是因为MagicMock
没有像函数那样实现描述符协议。
我如何包装一个方法并获得正确处理的
self
?在python3函数中实现描述符协议,从而拥有一个\uuuuuu获取
方法
def foo(自我):通过
...
foo.获得
因此,当一个函数作为属性附加到一个类上,并且您尝试从该类的实例中检索该方法时,将调用函数对象的\uuuu get\uu
方法。这就是self
如何绑定到第一个参数。例如,通过合成对foo
的\uuuu get\uuuu
的调用,我们可以得到一个版本的foo
,它似乎是一个字符串的方法
cb
被修补到对象中,并且由于cb
是一个函数,因此它正确地获取self
。当一个实例被修补时,这也起作用,因为直接在实例上设置属性时不使用描述符协议。这是否起作用<代码>使用wait_for_call(…)作为w:w()@Dan,如果我在那里传递self,它可能会有帮助,但这不是很有帮助,因为我通常在等待一个调用,它将深入调用图。
>>> a = "I am a string"
>>> foo.__get__(a,str)
<bound method foo of 'I am a string'>
>>> m = mock.MagicMock()
>>> m.foo()
<MagicMock name='mock.foo()' id='140643153651136'>
>>> m.foo.__get__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/unittest/mock.py", line 582, in __getattr__
raise AttributeError(name)
AttributeError: __get__
def wait_for_call(loop, obj, method, calls = 1):
# set up a future and call tracking
def cb(*args, **kwargs):
# Track number of calls and set a future when done
return wraps(*args, **kwargs)
try:
wraps = getattr(obj, method)
with mock.patch.object(obj, method,
new= cb):
yield
loop.run_until_complete(asyncio.wait_for(fut, 0.5))
except asyncio.futures.TimeoutError:
raise AssertionError("Timeout waiting for call to {} of {}".format(
method, obj)) from None