Наскільки я знаю, 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])
однак це не дозволяє аргументи, які можуть бути, наприклад, як int
a , так і 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))
1 Я використав назву Any
функції, оскільки вона "використовується як клас" у коді. Також any
є вбудованим ...
requests-mock
модуль також може бути цікавим.