Python 使用mock.patch.object在类的所有对象上包装方法

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

在一些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 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