Python mock call_args_list解包参数断言的元组

nac*_*son 37 python unit-testing python-unittest python-unittest.mock

我在处理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'))
Run Code Online (Sandbox Code Playgroud)

我想知道是否有更好的方法来解压缩模拟对象上的call_args_list以便进行断言.这个循环有效,但感觉必须有一个更直接的方式.

mgi*_*son 51

我认为这里的许多困难都包含在"呼叫"对象的处理中.它可以被认为是一个有2个成员的元组,(args, kwargs)因此解压缩它通常很好:

args, kwargs = call
Run Code Online (Sandbox Code Playgroud)

一旦它被解压缩,那么你可以分别为args和kwargs做出断言(因为一个是元组而另一个是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))
Run Code Online (Sandbox Code Playgroud)

请注意,有时简洁性没有帮助(例如,如果有错误):

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)
Run Code Online (Sandbox Code Playgroud)

  • 哦,“args, kwargs = call”是所有“call_args_list”黑客的线索。谢谢你! (2认同)

wim*_*wim 11

一个更好的方法可能是建立你自己的预期调用然后使用直接断言:

>>> 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)
Run Code Online (Sandbox Code Playgroud)

请注意,调用必须是顺序的,如果您不想这样,则将any_orderkwarg 覆盖到断言.

另请注意,允许在指定的呼叫之前或之后进行额外呼叫.如果你不想那样,你需要添加另一个断言:

>>> assert f.call_count == len(expected_calls)
Run Code Online (Sandbox Code Playgroud)

解决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)
Run Code Online (Sandbox Code Playgroud)

以及失败模式的示例:

>>> 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: []
Run Code Online (Sandbox Code Playgroud)