如何模拟绑定方法进行内省

How to mock bound method for introspection

我想测试下面的代码

import weakref

class WeakBoundMethod:
    """
    Wrapper around a method bound to a class instance. As opposed to bare
    bound methods, it holds only a weak reference to the `self` object,
    allowing it to be deleted.

    This can be useful when implementing certain kinds of systems that
    manage callback functions, such as an event manager.

    """
    def __init__(self, meth):
        """
        Initializes the class instance. It should be ensured that methods
        passed through the `meth` parameter are always bound methods. Static
        methods and free functions will produce an `AssertionError`.

        """
        assert (hasattr(meth, '__func__') and hasattr(meth, '__self__')),\
               'Object is not a bound method.'

        self._self = weakref.ref(meth.__self__)
        self._func = meth.__func__

    def __call__(self, *args, **kw):
        """
        Calls the bound method and returns whatever object the method returns.
        Any arguments passed to this will also be forwarded to the method.

        In case an exception is raised by the bound method, it will be
        caught and thrown again to the caller of this `WeakBoundMethod` object.

        Calling this on objects that have been collected will result in
        an `AssertionError` being raised.

        """        
        assert self.alive(), 'Bound method called on deleted object.'

        try:
            return self._func(self._self(), *args, **kw)
        except Exception as e:
            raise e

    def alive(self):
        """
        Checks whether the `self` object the method is bound to has
        been collected.

        """
        return self._self() is not None

我考虑过使用标准库中的 mock 来检查 __call__ 方法是否调用了所需的参数。我面临的问题是,如果我为绑定方法创建 Mock 对象,它没有 __self__ 或 __func__ 属性。所以我尝试了以下代码:

class TestWeakBoundMEthod(unittest.TestCase):
    def setUp(self):
        self.bm = Mock(name='bound method')
        self.bm.__self__ = Mock(name='self')
        self.bm.__func__ = Mock(name='func')

    def test_call(self):
        wbm = events.WeakBoundMethod(self.bm)
        wbm()
        self.bm.__func__.assert_called_with(self.bm.__self__)
        wbm(1, 2, 3)
        self.bm.__func__.assert_called_with(self.bm.__self__, 1, 2, 3)

它有效,但我觉得我没有正确测试。我在 class WeakBoundMethod 如何工作方面使用了太多知识,而不是测试实际结果。

是否有更好的方法来模拟绑定方法?我应该用虚拟方法制作虚拟 class 吗?

首先:如果您使用的是 python-3.4,为什么不使用 WeakMethod from weakref

无论如何,模拟框架是一种强大的方法,但有时可能是一种矫枉过正的方法,它会迫使您了解太多要测试的内容:副作用可能是您的测试与实现的耦合度太高。

你的情况最好用实物来测试一下。对您的 WeakBoundMethod 的一个相当完整的测试可以是这样的:

class TestWeakMethod(unittest.TestCase):
    def test_call(self):
        class C():
            def c(self,*args,**kwargs):
                return (args,kwargs)
        c=C()
        wc = WeakBoundMethod(c.c)
        self.assertEqual(((1,2,3),{"a":11,"b":"33"}), wc(1,2,3,a=11,b="33"))

    def test_just_bound_method(self):
        c=object()
        self.assertRaises(Exception, WeakBoundMethod, c)
        self.assertRaises(Exception, WeakBoundMethod, object)
        def f():
            pass
        self.assertRaises(Exception, WeakBoundMethod, f)

    def test_is_really_weak(self):
        class C():
            DEAD=False
            def c(self,*args,**kwargs):
                return (args,kwargs)
            def __del__(self):
                C.DEAD = True
        c=C()
        wc = WeakBoundMethod(c.c)
        del c

        self.assertTrue(C.DEAD)
        self.assertRaises(Exception, wc)

我希望足够清楚:我喜欢 mock 框架并且我正在非常频繁地使用它,但是如果创建可以感知和报告您的内容的真实对象并不难,那么您不应该使用它想测试一下。