2017-05-30 4 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() 

TypeErrorx.barためにはselfを取得できません:しかし、私はこのような定期的な(インスタンス)メソッドにパッチを適用する例がいくつかあります。私は、MagicMockは、関数と同じように記述子プロトコルを実装していないためだと思います。 メソッドをラップして、selfを正しく処理するにはどうすればよいですか?

+0

これは機能しますか? 'w()'としてwait_for_call(...)をつけています。 – Dan

+0

@Danもし私がそこに自信を持っていたら、それはおそらく役に立ちますが、私は通話中に深刻になるだろうグラフ。 –

答えて

0

python3の関数では、記述子プロトコルが実装されているため、__get__メソッドがあります。

デフfooの(自己): ... のfooを渡します。 get したがって、関数がクラスとして属性としてアタッチされていて、そのクラスのインスタンスからそのメソッドを取得しようとすると、関数オブジェクトの__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__ 

をはるかに簡単があります質問の目標を達成するために、明示的にMagicMockに__get__を設定することが可能に見えますが、アプローチ。 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を取得しています。これは、属性がインスタンス上で直接設定されているときに記述子プロトコルが使用されないため、インスタンスがパッチ適用されたときにも機能します。

関連する問題