简体   繁体   English

Python模拟call_args_list拆包元组以声明参数

[英]Python mock call_args_list unpacking tuples for assertion on arguments

I'm having some trouble dealing with the nested tuple which Mock.call_args_list returns. 我在处理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'))

I would like to know if there is a better way to unpack that call_args_list on the mock object in order to make my assertion. 我想知道是否有更好的方法可以在模拟对象上解压缩该call_args_list,以便进行断言。 This loop works, but it feels like there must be a more straight forward way. 此循环有效,但感觉必须有更直接的方法。

I think that many of the difficulties here are wrapped up in the treatment of the "call" object. 我认为,这里的许多困难都笼罩在处理“通话”对象中。 It can be thought of as a tuple with 2 members (args, kwargs) and so it's frequently nice to unpack it: 可以将其视为具有2个成员(args, kwargs)的元组,因此通常将其拆包是很不错的:

args, kwargs = call

Once it's unpacked, then you can make your assertions separately for args and kwargs (since one is a tuple and the other a dict) 解压缩后,就可以分别对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))

Note that sometimes the terseness isn't helpful (eg if there is an error): 请注意,有时简洁性无济于事(例如,如果有错误):

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)

A nicer way might be to build up the expected calls your self then use a direct assertion: 更好的方法可能是建立您自己的预期调用,然后使用直接断言:

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

Note that the calls must be sequential, if you don't want that then override the any_order kwarg to the assertion. 请注意,调用必须是顺序的,如果您不希望这样,则将any_order kwarg覆盖到断言。

Also note that it's permitted for there to be extra calls before or after the specified calls. 另请注意,允许在指定呼叫之前或之后进行额外的呼叫。 If you don't want that, you'll need to add another assertion: 如果您不希望这样做,则需要添加另一个断言:

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

Addressing the comment of mgilson, here's an example of creating a dummy object that you can use for wildcard equality comparisons: 在谈到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)

And an example of the failure mode: 以及失败模式的示例:

>>> 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: []

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM