use*_*661 57 python unit-testing
我正在嘲笑requests.post使用该Mock库的电话:
requests.post = Mock()
Run Code Online (Sandbox Code Playgroud)
调用涉及多个参数:URL,有效负载,一些auth等等.我想断言requests.post用特定的URL调用,但我不关心其他参数.当我尝试这个:
requests.post.assert_called_with(requests_arguments)
Run Code Online (Sandbox Code Playgroud)
测试失败,因为它期望仅使用该参数调用它.
有没有办法检查函数调用中某处是否使用了单个参数而不必传入其他参数?
或者,更好的是,有没有办法断言一个特定的URL,然后为其他参数抽象数据类型(即数据应该是字典,auth应该是HTTPBasicAuth的实例,等等)?
k0n*_*0nG 156
您还可以使用ANY帮助程序始终匹配您不知道或不检查的参数.
更多关于ANY的帮助:https: //docs.python.org/3/library/unittest.mock.html#any
所以例如你可以将参数'session'与之类似:
from unittest.mock import ANY
requests_arguments = {'slug': 'foo', 'session': ANY}
requests.post.assert_called_with(requests_arguments)
Run Code Online (Sandbox Code Playgroud)
Bak*_*riu 42
据我所知Mock,没有提供一种方法来实现你想要的东西assert_called_with.您可以访问call_args和call_args_list成员并手动执行断言.
然而,这是一种简单(和肮脏)的方式来实现你想要的几乎.您必须实现一个__eq__方法始终返回的类True:
def Any(cls):
class Any(cls):
def __eq__(self, other):
return True
return Any()
Run Code Online (Sandbox Code Playgroud)
用它作为:
In [14]: caller = mock.Mock(return_value=None)
In [15]: caller(1,2,3, arg=True)
In [16]: caller.assert_called_with(Any(int), Any(int), Any(int), arg=True)
In [17]: caller.assert_called_with(Any(int), Any(int), Any(int), arg=False)
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-17-c604faa06bd0> in <module>()
----> 1 caller.assert_called_with(Any(int), Any(int), Any(int), arg=False)
/usr/lib/python3.3/unittest/mock.py in assert_called_with(_mock_self, *args, **kwargs)
724 if self.call_args != (args, kwargs):
725 msg = self._format_mock_failure_message(args, kwargs)
--> 726 raise AssertionError(msg)
727
728
AssertionError: Expected call: mock(0, 0, 0, arg=False)
Actual call: mock(1, 2, 3, arg=True)
Run Code Online (Sandbox Code Playgroud)
你可以看到它只检查arg.你必须创建子类int,否则比较不会工作1.但是,您仍然必须提供所有参数.如果您有许多参数,可以使用tuple-unpacking缩短代码:
In [18]: caller(1,2,3, arg=True)
In [19]: caller.assert_called_with(*[Any(int)]*3, arg=True)
Run Code Online (Sandbox Code Playgroud)
除此之外,我无法想到一种方法来避免将所有参数传递给您assert_called_with并按照您的意图使用它.
可以扩展上述解决方案以检查其他参数的类型.例如:
In [21]: def Any(cls):
...: class Any(cls):
...: def __eq__(self, other):
...: return isinstance(other, cls)
...: return Any()
In [22]: caller(1, 2.0, "string", {1:1}, [1,2,3])
In [23]: caller.assert_called_with(Any(int), Any(float), Any(str), Any(dict), Any(list))
In [24]: caller(1, 2.0, "string", {1:1}, [1,2,3])
In [25]: caller.assert_called_with(Any(int), Any(float), Any(str), Any(dict), Any(tuple))
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-25-f607a20dd665> in <module>()
----> 1 caller.assert_called_with(Any(int), Any(float), Any(str), Any(dict), Any(tuple))
/usr/lib/python3.3/unittest/mock.py in assert_called_with(_mock_self, *args, **kwargs)
724 if self.call_args != (args, kwargs):
725 msg = self._format_mock_failure_message(args, kwargs)
--> 726 raise AssertionError(msg)
727
728
AssertionError: Expected call: mock(0, 0.0, '', {}, ())
Actual call: mock(1, 2.0, 'string', {1: 1}, [1, 2, 3])
Run Code Online (Sandbox Code Playgroud)
但是这不允许参数可以是例如inta或a str.允许多个参数Any和使用多继承不会有帮助.我们可以解决这个问题abc.ABCMeta
def Any(*cls):
class Any(metaclass=abc.ABCMeta):
def __eq__(self, other):
return isinstance(other, cls)
for c in cls:
Any.register(c)
return Any()
Run Code Online (Sandbox Code Playgroud)
例:
In [41]: caller(1, "ciao")
In [42]: caller.assert_called_with(Any(int, str), Any(int, str))
In [43]: caller("Hello, World!", 2)
In [44]: caller.assert_called_with(Any(int, str), Any(int, str))
Run Code Online (Sandbox Code Playgroud)
1我使用了Any该函数的名称,因为它在代码中"用作类".也是any一个内置...
小智 17
@mock.patch.object(module, 'ClassName')
def test_something(self, mocked):
do_some_thing()
args, kwargs = mocked.call_args
self.assertEqual(expected_url, kwargs.get('url'))
Run Code Online (Sandbox Code Playgroud)
域名注册地址:
args, kwargs = requests.post.call_args_list[-1]
self.assertTrue('slug' in kwargs, '`slug` not passed to requests.post')
Run Code Online (Sandbox Code Playgroud)
简单来说,我们可以访问一个包含所有位置参数的元组和一个包含所有传递给函数的命名参数的字典——所以现在你可以检查任何你想要的。
我发现这种方法比其他流行的答案更简洁,因为:
如果传递的参数太多,而只检查其中一个,则执行类似操作{'slug': 'foo', 'field1': ANY, 'field2': ANY, 'field3': ANY, ' . . . }可能会很笨拙。
此外,如果您想检查几个字段的数据类型:
args, kwargs = requests.post.call_args_list[0]
self.assertTrue(isinstance(kwargs['data'], dict))
Run Code Online (Sandbox Code Playgroud)
此外,如果您传递参数(而不是关键字参数),您可以通过args如下方式访问它们:
self.assertEqual(
len(args), 1,
'post called with different number of arguments than expected'
)
Run Code Online (Sandbox Code Playgroud)