如何对装饰方法进行单元测试

时间:2017-02-22 10:10:17

标签: python unit-testing mocking python-decorators

假设我必须进行单元测试methodA,在以下类中定义:

class SomeClass(object):

    def wrapper(fun):
        def _fun(self, *args, **kwargs):
            self.b = 'Original'
            fun(self, *args, **kwargs)
        return _fun

    @wrapper
    def methodA(self):
        pass

我的测试课如下:

from mock import patch

class TestSomeClass(object):

    def testMethodA(self):
        def mockDecorator(f):
            def _f(self, *args, **kwargs):
                self.b = 'Mocked'
                f(self, *args, **kwargs)
            return _f

        with patch('some_class.SomeClass.wrapper', mockDecorator):
            from some_class import SomeClass
            s = SomeClass()
            s.methodA()
            assert s.b == 'Mocked', 's.b is equal to %s' % s.b

如果我运行测试,我会点击断言:

File "/home/klinden/workinprogress/mockdecorators/test_some_class.py", line 17, in testMethodA
    assert s.b == 'Mocked', 's.b is equal to %s' % s.b
AssertionError: s.b is equal to Original

如果我在测试中粘贴了一个断点,在修补之后,我可以看到wrapper被嘲笑得很好,但是methodA仍然引用旧的包装器:

(Pdb) p s.wrapper
<bound method SomeClass.mockDecorator of <some_class.SomeClass object at 0x7f9ed1bf60d0>>
(Pdb) p s.methodA
<bound method SomeClass._fun of <some_class.SomeClass object at 0x7f9ed1bf60d0>>

知道问题出在这里吗?

1 个答案:

答案 0 :(得分:0)

仔细研究后,我找到了解决方案。

因为猴子补丁似乎没有效果(我也试过a few other解决方案),我挖掘了函数内部结构,这证明是富有成效的。

Python 3 你很幸运 - 只需使用wraps装饰器,它创建一个__wrapped__属性,该属性又包含包装函数。有关更多详细信息,请参阅上面的链接答案。

Python 2 即使您使用@wraps,也不会创建任何花哨的属性。 但是,您只需要意识到包装器方法只执行闭包:因此您将能够在其func_closure属性中找到包装函数。

在原始示例中,包装函数位于:s.methodA.im_func.func_closure[0].cell_contents

结束(哈!) 我沿着这条线创建了一个getWrappedFunction帮助器,以方便我的测试:

@staticmethod
def getWrappedFunction(wrapper):
    return wrapper.im_func.func_closure[0].cell_contents

YMMV,特别是如果你喜欢花哨的东西,并在封闭中包含其他物体。