Порядок вызова метода шаблона в python
Каков был бы самый простой способ запустить функцию модульного тестирования? Здесь меня не интересует, что делают fun* функции, но только в том случае, если порядок их вызова правильный.
from mock import Mock
(many, arguments, four, fun) = ('many', 'arguments', 'four', 'fun')
class TemplateMethod(object):
def fun1(self, many, parameters, go, here):
raise NotImplementedError()
@classmethod
def fun2(cls, many, parameters, go, here):
pass
@staticmethod
def fun3(many, parameters, go, here):
pass
def run(self):
result1 = self.fun1(many, arguments, four, fun)
result1.do_something()
self.fun2(many, arguments, four, fun)
self.fun3(many, arguments, four, fun)
Единственное требование к решению - это не навязчивость к тестируемому классу.
РЕШЕНИЕ:
На самом деле это отчасти черновик, и этот простой класс можно исправить и расширить таким образом, что мне даже наплевать на это. Дело в том, что теперь вы можете просто записать все вызовы всех методов в шаблонном методе. Вы также можете указать список объектов, которые не должны быть смоделированы классом (т. Е. Функция, для которой вы записываете вызовы).
Особая благодарность @Damian Schenkelman, который дал мне несколько важных советов.
class MockingInvocationRecorder(object):
FUNCTION_TYPES = ('function', 'classmethod', 'staticmethod')
def __init__(self, obj, dont_mock_list):
self._invocation_list = []
name_list = [exc.__name__ for exc in dont_mock_list]
self._wrap_memfuns(obj, name_list)
@property
def invocations(self):
return tuple(self._invocation_list)
def _wrap_single(self, memfun, exclude_list):
def wrapper(*args, **kwargs):
self._invocation_list.append(memfun.__name__)
if memfun.__name__ in exclude_list:
return memfun(*args, **kwargs)
else:
return Mock()
return wrapper
def _wrap_memfuns(self, obj, exclude_list):
for (mem_name, mem) in type(obj).__dict__.iteritems():
if type(mem).__name__ in self.FUNCTION_TYPES:
wrapper = self._wrap_single(getattr(obj, mem_name), exclude_list)
setattr(obj, mem_name, wrapper)
Теперь вы можете проверить порядок вызовов следующим образом:
tm = TemplateMethod()
ir = MockingInvocationRecorder(tm, [tm.run])
tm.run()
print ir.invocations => ('run', 'fun1', 'fun2', 'fun3')
Вы можете ссылаться на члена по имени класса:
ir = MockingInvocationRecorder(tm, [TemplateMethod.run])
Только будьте осторожны, чтобы вы включили все методы, которые не должны быть издевались.
2 ответа
Monkeypatch fun1, fun2, fun3 функции и использовать список для отслеживания порядка вызовов. После этого утверждают в списке.
Использование __call__
может быть полезным здесь
class Function1(object):
def __call__(self, params, logger=None):
if logger is not None:
logger.append(self.__repr__())
return self.function1(params)
# Or no return if only side effects are needed
def __init__(self, ...):
#...
def __repr__(self):
# Some string representation of your function.
def function1(params):
print "Function 1 stuff here..."
Тогда сделай что-нибудь вроде
class TemplateMethod(object):
def __init__(self, params):
self.logger = []
self.fun1 = Function1(...)
self.fun1(params, self.logger)
Это довольно забавно; вероятно, есть несколько способов очистить то, к чему я стремлюсь, но инкапсуляция функций в классах и затем использование __call__
это хороший путь.