假设我必须进行单元测试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>>
知道问题出在这里吗?
答案 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,特别是如果你喜欢花哨的东西,并在封闭中包含其他物体。