2017-05-30 39 views
0

在一些asyncio测试用例中,我发现我经常想等待某个方法被调用,所以我编写了下面的上下文管理器:使用mock.patch.object包装一个类的所有对象的方法

@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的正确处理?

+0

这是行不通的? '用wait_for_call(...)作为w:w()' – Dan

+0

@Dan如果我在那里传递自己,它可能会,但它不是很有用,因为我通常在等待一个在通话中很深的通话图形。 –

回答

0

在python3函数实现描述符协议,因此有一个__get__方法。

高清富(个体经营):通过 ... FOO。 得到 所以,当一个函数作为一个属性附加到一个类上,并且您尝试从该类的一个实例中检索该方法时,将调用该函数对象的__get__方法。这是如何将self绑定到第一个参数。例如,通过合成对foo__get__的调用,我们可以获得foo的一个版本,该版本似乎是字符串的一种方法。

>>> a = "I am a string" 
>>> foo.__get__(a,str) 
<bound method foo of 'I am a string'> 

MagicMock不这样做:

>>> 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__ 

这似乎可以明确地设置__get__上MagicMock,但要实现这一问题的目标还有一个更简单做法。 patch.object方法可用于修补任何东西,而不仅仅是一个MagicMock。如果MagicMock的功能都不需要,那么你可以简单地用一种新方法打补丁。

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 

cb修补到物体并且由于cb是一个函数,它正确地得到self。这也适用于实例修补时,因为在实例上直接设置属性时不使用描述符协议。

相关问题