unittest.mock:声明方法参数的部分匹配

jpa*_*kal 41 python unit-testing mocking

Rubyist在这里编写Python.我有一些看起来像这样的代码:

result = database.Query('complicated sql with an id: %s' % id)
Run Code Online (Sandbox Code Playgroud)

database.Query被模拟了,我想测试ID是否被正确注入而不将整个SQL语句硬编码到我的测试中.在Ruby/RR中,我会这样做:

mock(database).query(/#{id}/)
Run Code Online (Sandbox Code Playgroud)

但是我无法在unittest.mock中看到像这样设置'选择性模拟'的方法,至少没有一些毛茸茸的side_effect逻辑.所以我尝试在断言中使用正则表达式:

with patch(database) as MockDatabase:
  instance = MockDatabase.return_value
  ...
  instance.Query.assert_called_once_with(re.compile("%s" % id))
Run Code Online (Sandbox Code Playgroud)

但这也不起作用.这种方法确实有效,但它很难看:

with patch(database) as MockDatabase:
  instance = MockDatabase.return_value
  ...
  self.assertIn(id, instance.Query.call_args[0][0])
Run Code Online (Sandbox Code Playgroud)

好主意?

fal*_*tru 61

import mock

class AnyStringWith(str):
    def __eq__(self, other):
        return self in other

...
result = database.Query('complicated sql with an id: %s' % id)
database.Query.assert_called_once_with(AnyStringWith(id))
...
Run Code Online (Sandbox Code Playgroud)

编辑:抢先需要匹配的字符串

def arg_should_contain(x):
    def wrapper(arg):
        assert str(x) in arg, "'%s' does not contain '%s'" % (arg, x)
    return wrapper

...
database.Query = arg_should_contain(id)
result = database.Query('complicated sql with an id: %s' % id)
Run Code Online (Sandbox Code Playgroud)

  • 也许聚会有点晚了,但我刚刚发布了一个正是这样的库:) 因此,一个无耻的插件:https://github.com/Xion/callee (2认同)

小智 17

你可以使用unittest.mock.ANY:)

from unittest.mock import Mock, ANY

def foo(some_string):
    print(some_string)

foo = Mock()
foo("bla")
foo.assert_called_with(ANY)
Run Code Online (Sandbox Code Playgroud)

如此处所述 - https://docs.python.org/3/library/unittest.mock.html#any

  • 你只写了一个更长版本的 `assert foo.used` (5认同)
  • 那断言已经发送了一些东西,但是没有检查ID是否通过。 (3认同)

saa*_*kis 6

您可以使用match_equalityPyHamcrest来包装matches_regexp同一库中的匹配器:

from hamcrest.library.integration import match_equality

with patch(database) as MockDatabase:
  instance = MockDatabase.return_value
  ...
  expected_arg = matches_regexp(id)
  instance.Query.assert_called_once_with(match_equality(expected_arg))

Run Code Online (Sandbox Code Playgroud)

Python 的文档中也提到了这个方法unittest.mock

从版本 1.5 开始,Python 测试库 PyHamcrest 提供了类似的功能,在这里可能有用,其形式是等式匹配器 (hamcrest.library.integration.match_equality)。

如果您不想使用 PyHamcrest,上面链接的文档还展示了如何通过定义带有__eq__方法的类来编写自定义匹配器(如falsetru答案中所建议的):

class Matcher:
    def __init__(self, compare, expected):
        self.compare = compare
        self.expected = expected

    def __eq__(self, actual):
        return self.compare(self.expected, actual)

match_foo = Matcher(compare, Foo(1, 2))
mock.assert_called_with(match_foo)
Run Code Online (Sandbox Code Playgroud)

self.compare您可以用您自己的正则表达式匹配替换对此处的调用,False如果没有找到则返回,或者引发AssertionError带有您选择的描述性错误消息的 。