web-dev-qa-db-ja.com

Pythonモックcall_args_list引数のアサーションのためのタプルのアンパック

ネストされたタプルを処理するのに問題がありますMock.call_args_list 返却値。

def test_foo(self):
    def foo(fn):
        fn('PASS and some other stuff')

    f = Mock()
    foo(f)
    foo(f)
    foo(f)

    for call in f.call_args_list:
        for args in call:
            for arg in args:
                self.assertTrue(arg.startswith('PASS'))

アサーションを作成するために、モックオブジェクトでそのcall_args_listを展開するより良い方法があるかどうかを知りたいです。このループは機能しますが、もっと簡単な方法が必要だと感じています。

34
nackjicholson

ここでの困難の多くは、「呼び出し」オブジェクトの処理にまとめられていると思います。 2つのメンバーを持つタプルと考えることができます(args, kwargs)そして、それをアンパックするのはよくナイスです:

args, kwargs = call

展開したら、引数とkwargsのアサーションを別々に作成できます(1つはTupleであり、もう1つはdictであるため)

def test_foo(self):
    def foo(fn):
        fn('PASS and some other stuff')

    f = Mock()
    foo(f)
    foo(f)
    foo(f)

    for call in f.call_args_list:
        args, kwargs = call
        self.assertTrue(all(a.startswith('PASS') for a in args))

簡潔さは役に立たない場合があることに注意してください(エラーがある場合など)。

for call in f.call_args_list:
    args, kwargs = call
    for a in args:
        self.assertTrue(a.startswith('PASS'), msg="%s doesn't start with PASS" % a)
48
mgilson

より良い方法は、予想される呼び出しを構築し、直接アサーションを使用することです。

>>> from mock import call, Mock
>>> f = Mock()
>>> f('first call')
<Mock name='mock()' id='31270416'>
>>> f('second call')
<Mock name='mock()' id='31270416'>
>>> expected_calls = [call(s + ' call') for s in ('first', 'second')]
>>> f.assert_has_calls(expected_calls)

呼び出しがシーケンシャルでなければならないことに注意してください。それが必要ない場合は、any_orderアサーションに敬意を表します。

また、指定された呼び出しの前後に余分な呼び出しがあることが許可されていることに注意してください。そうしたくない場合は、別のアサーションを追加する必要があります。

>>> assert f.call_count == len(expected_calls)

Mgilsonのコメントに対処するために、ワイルドカードの等価比較に使用できるダミーオブジェクトを作成する例を次に示します。

>>> class AnySuffix(object):
...     def __eq__(self, other):
...         try:
...             return other.startswith('PASS')
...         except Exception:
...             return False
...        
>>> f = Mock()
>>> f('PASS and some other stuff')
<Mock name='mock()' id='28717456'>
>>> f('PASS more stuff')
<Mock name='mock()' id='28717456'>
>>> f("PASS blah blah don't care")
<Mock name='mock()' id='28717456'>
>>> expected_calls = [call(AnySuffix())]*3
>>> f.assert_has_calls(expected_calls)

失敗モードの例:

>>> Mock().assert_has_calls(expected_calls)
AssertionError: Calls not found.
Expected: [call(<__main__.AnySuffix object at 0x1f6d750>),
 call(<__main__.AnySuffix object at 0x1f6d750>),
 call(<__main__.AnySuffix object at 0x1f6d750>)]
Actual: []
10
wim