Ответ 1
Насколько я знаю, 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
и 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
встроенный в...