여러 인수 중 하나를 사용하여 메서드가 호출되었음을 확인
라이브러리 requests.post
사용에 대한 호출을 조롱 하고 Mock
있습니다.
requests.post = Mock()
호출에는 URL, 페이로드, 일부 인증 항목 등 여러 인수가 포함됩니다 requests.post
. 특정 URL로 호출되는 것을 주장하고 싶지만 다른 인수는 신경 쓰지 않습니다. 내가 이것을 시도 할 때 :
requests.post.assert_called_with(requests_arguments)
테스트는 해당 인수로만 호출 될 것으로 예상하므로 실패합니다.
다른 인수를 전달하지 않고도 함수 호출의 어딘가에 단일 인수가 사용되는지 확인할 수있는 방법이 있습니까?
또는 더 좋은 방법은 특정 URL을 주장한 다음 다른 인수에 대한 데이터 유형을 추상화하는 방법이 있습니까 (예 : 데이터는 사전이어야하고 인증은 HTTPBasicAuth의 인스턴스 여야 함)?
내가 아는 한을 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()
다음과 같이 사용 :
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)
보시다시피 arg
. 의 하위 클래스를 만들어야합니다 int
. 그렇지 않으면 비교가 작동하지 않습니다 1 . 그러나 여전히 모든 인수를 제공해야합니다. 인수가 많으면 튜플 풀기를 사용하여 코드를 줄일 수 있습니다.
In [18]: caller(1,2,3, arg=True)
In [19]: caller.assert_called_with(*[Any(int)]*3, arg=True)
이것을 제외하고는 모든 매개 변수를 전달하는 것을 피하고 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])
그러나 이것은 예를 들어 an int
또는 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()
예:
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))
1Any
함수 의 이름 은 코드에서 "클래스로 사용"되었기 때문에 사용했습니다. 또한 any
내장 ...
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)
@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'))
참조 : 튜플로 호출
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.
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 args
like this
self.assertEqual(
len(args), 1,
'post called with different number of arguments than expected'
)
You can use : 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)
ReferenceURL : https://stackoverflow.com/questions/21611559/assert-that-a-method-was-called-with-one-argument-out-of-several
'programing tip' 카테고리의 다른 글
웹 페이지에서 위쪽 여백을 제거하려면 어떻게합니까? (0) | 2021.01.09 |
---|---|
Iphone의 UiButton에 텍스트 여백 / 패딩 제공 (0) | 2021.01.09 |
C ++ 템플릿은 단지 매크로 일 뿐입니 까? (0) | 2021.01.09 |
새롭고 미성숙 한 기술로 손을 태운 적이 있습니까? (0) | 2021.01.09 |
모달보기 컨트롤러의 iPad 사용자 정의 크기 (0) | 2021.01.09 |