如何检查带通配符的模拟调用?

32 投票
3 回答
15173 浏览
提问于 2025-04-17 20:51

我正在写单元测试,想要检查一个调用,这个调用里有一个函数对象,像这样:

call(u'mock', u'foobar', <function <lambda> at 0x1ea99b0>, 10)

我该如何检查这个调用(call())是否包含我想要的所有参数,而不需要重新写这个lambda函数呢?

补充说明:我想澄清一下,我使用的是这个mock库:http://mock.readthedocs.org/en/latest/。我上面提到的call是一个MagicMock对象上的调用,我想用assert_has_calls来检查它。

3 个回答

2

我不太确定你是怎么构建这个 call 的,但如果它是某种 args 的话:

# IN THE CASE WE'RE DOING call(*args)

if all([len(args) == 4,isinstance(args[0],str),
       isinstance(args[1],str), hasattr(args[2],'__call__'),
       isinstance(args[3],int)]):
    # PASS
else:
    # FAIL

如果你特别担心输入的内容是一个可调用的东西,但它不是一个函数,并且你觉得这样会在单元测试中默默失败:

from types import FunctionType

isinstance(lambda x: x,FunctionType) # True
16

如果你想要比mock.ANY更细致的控制,可以自己创建一个验证器类,用于在调用比较时,比如assert_has_calls、assert_called_once_with等。

class MockValidator(object):

    def __init__(self, validator):
        # validator is a function that takes a single argument and returns a bool.
        self.validator = validator

    def __eq__(self, other):
        return bool(self.validator(other))

你可以像这样使用:

import mock
my_mock = mock.Mock()
my_mock('foo', 8)

# Raises AssertionError.
my_mock.assert_called_with('foo', MockValidator(lambda x: isinstance(x, str)))

# Does not raise AssertionError.
my_mock.assert_called_with('foo', MockValidator(lambda x: isinstance(x, int)))
52

我终于找到了实现我想要的方式。基本上,当我使用 assert_has_calls 时,我希望有一个参数可以不管是什么都能匹配(因为在测试时我不能每次都重新创建 lambda)。

实现这个的方法是使用 mock.ANY

所以,在我的例子中,这可以匹配以下的调用:

mocked_object.assert_has_calls([
   call('mock', 'foobar', mock.ANY, 10)
])

撰写回答