2

実行関数をユニットテストする最も簡単な方法は何でしょうか?ここでは、 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])

嘲笑されるべきではないすべてのメソッドを参加させるように注意してください。

4

2 に答える 2

3

モンキーパッチfun1、fun2、fun3は機能し、リストを使用して呼び出し順序を追跡します。その後、リストでアサートします。

于 2012-09-22T16:22:32.913 に答える
2

ここで利用する__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__するのが良い方法です。

于 2012-09-22T20:43:44.543 に答える