Python 断言一个方法是用几个参数中的一个参数调用的
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21611559/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Assert that a method was called with one argument out of several
提问by user1427661
I'm mocking out a call to requests.postusing the Mocklibrary:
我正在模拟调用requests.post使用Mock库:
requests.post = Mock()
The the call involves multiple arguments: the URL, a payload, some auth stuff, etc. I want to assert that requests.postis called with a particular URL, but I don't care about the other arguments. When I try this:
该调用涉及多个参数:URL、有效负载、一些身份验证内容等。我想断言requests.post使用特定 URL 调用它,但我不关心其他参数。当我尝试这个时:
requests.post.assert_called_with(requests_arguments)
the test fails, as it expects it to be called with only that argument.
测试失败,因为它期望它只用那个参数来调用。
Is there any way to check if a single argument is used somewhere in the function call without having to pass in the other arguments?
有什么方法可以检查函数调用中是否使用了单个参数而不必传入其他参数?
Or, even better, is there a way to assert a specific URL and then abstract data types for the other arguments (i.e., data should be a dictionary, auth should be an instance of HTTPBasicAuth, etc.)?
或者,更好的是,有没有办法断言特定的 URL,然后为其他参数抽象数据类型(即,数据应该是字典,身份验证应该是 HTTPBasicAuth 的实例,等等)?
采纳答案by Bakuriu
As far as I know Mockdoesn't provide a way to achieve what you want via assert_called_with. You could access the call_argsand call_args_listmembers and perform the assertions manually.
据我所知Mock,没有提供通过assert_called_with. 您可以访问call_args和call_args_list成员并手动执行断言。
However the is a simple (and dirty) way of achieving almostwhat you want. You have to implement a class whose __eq__method always returns True:
然而,这是实现几乎你想要的东西的一种简单(和肮脏)的方式。您必须实现一个其__eq__方法始终返回的类True:
def Any(cls):
class Any(cls):
def __eq__(self, other):
return True
return Any()
Using it as:
将其用作:
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)
As you can see it only checks for the arg. You have to create subclasses of int, otherwise the comparisons wont work1. However you still have to provide all the arguments. If you have many arguments you might shorten your code using tuple-unpacking:
如您所见,它仅检查arg. 您必须创建 的子类int,否则比较将不起作用1。但是,您仍然必须提供所有参数。如果你有很多参数,你可以使用元组解包来缩短你的代码:
In [18]: caller(1,2,3, arg=True)
In [19]: caller.assert_called_with(*[Any(int)]*3, arg=True)
Except for this I cannot think of a way to avoid passing all parameters to assert_called_withand work it as you intend.
除此之外,我想不出一种方法来避免将所有参数传递给assert_called_with您并按您的意愿工作。
The above solution can be extended to check for types of other arguments. For example:
上述解决方案可以扩展到检查其他参数的类型。例如:
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])
however this doesn't allow arguments that can be, for example, both an intor a str. Allowing multiple arguments to Anyand using multiple-inheritance wont help. We can solve this using abc.ABCMeta
但是,这不允许参数可以同时是 anint或 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()
Example:
例子:
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))
1I used the name Anyfor the function since it is "used as a class" in the code. Also anyis a built-in...
1我使用了Any函数的名称,因为它在代码中“用作类”。还有any一个内置...
回答by k0nG
You can also use the ANYhelper to always match arguments you don't know or aren't checking for.
您还可以使用ANY帮助程序始终匹配您不知道或未检查的参数。
More on the ANY helper: https://docs.python.org/3/library/unittest.mock.html#any
有关 ANY 助手的更多信息:https: //docs.python.org/3/library/unittest.mock.html#any
So for instance you could match the argument 'session' to anything like so:
因此,例如,您可以将参数 'session' 与以下内容进行匹配:
from unittest.mock import ANY
requests_arguments = {'slug': 'foo', 'session': ANY}
requests.post.assert_called_with(requests_arguments)
回答by ZhiQiang Fan
@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'))
see: calls-as-tuples
请参阅:调用元组
回答by Mounirsky
You can use : assert_any_call(args) https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_any_call
您可以使用:assert_any_call(args) https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_any_call
requests.post.assert_any_call(requests_arguments)
requests.post.assert_any_call(requests_arguments)
回答by akki
If there are too many parameters being passed and only one of them is to be checked, doing something like {'slug': 'foo', 'field1': ANY, 'field2': ANY, 'field3': ANY, ' . . . }can be clumsy.
如果传递的参数太多,而只检查其中一个,则执行此类操作{'slug': 'foo', 'field1': ANY, 'field2': ANY, 'field3': ANY, ' . . . }可能会很笨拙。
I took the following approach to achieve this:
我采取了以下方法来实现这一目标:
args, kwargs = requests.post.call_args_list[0]
self.assertTrue('slug' in kwargs, 'Slug not passed to requests.post')
In simple words, this returns a tuple with all positional arguments and dictionary with all named arguments passed to the function call, so now you can check anything you want.
简单来说,这将返回一个包含所有位置参数和字典的元组,其中所有命名参数都传递给函数调用,因此现在您可以检查任何您想要的内容。
Furthermore, if you wanted to check the datatype of a few fields
此外,如果您想检查几个字段的数据类型
args, kwargs = requests.post.call_args_list[0]
self.assertTrue((isinstance(kwargs['data'], dict))
Also, if you're passing arguments (instead of keyword arguments), you can access them via argslike this
此外,如果您要传递参数(而不是关键字参数),则可以args像这样访问它们
self.assertEqual(
len(args), 1,
'post called with different number of arguments than expected'
)

