実行関数をユニットテストする最も簡単な方法は何でしょうか?ここでは、 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])
嘲笑されるべきではないすべてのメソッドを参加させるように注意してください。