(Nota bene:这是对原始问题进行了大量修改,包括我错误地忽略的细节。)
这是我正在测试的(汇总)文件(common.py
)。它包含一个装饰器(派生自Decorum
library),它调用另一个对象(A
)上的类方法:我想修补A
,因为该代码进行外部调用我'我没有测试。
from decorum import Decorum
class A:
@classmethod
def c(cls):
pass
class ClassyDecorum(Decorum):
"""Hack to allow decorated instance methods of a class object to run with decorators.
Replace this once Decorum 1.0.4+ comes out.
"""
def __get__(self, instance, owner):
from functools import partial
return partial(self.call, instance)
class B(Decorum):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def init(self, *args, **kwargs):
A.c()
return super().init(*args, **kwargs)
我希望在我的单元测试中@patch
类A
来隔离并检查B.d()
的功能。这是我的unittest(位于test/test_common.py
):
class BDecoratedClass(MagicMock):
@B
def dummy_func(self):
return "Success"
class TestB(TestCase):
@patch('unittest_experiment.A', autospec=True)
def test_d(self, mock_a):
b = BDecoratedClass()
b.dummy_func()
mock_a.c.assert_called_once_with() # Fails
调试上面的内容,我发现A
实际上从未被模拟过:代码进入A
的代码,因此从不调用mock_a
是有意义的,因此断言失败。但是,我想正确地修补补丁A
。如果我正在修补common.py
中存在的导入,这种方法有效,但显然不是在那里定义了类吗?
请注意,我认为这可能是 我正在修补的问题,@patch('common.A', autospec=True)
应该更像是@patch('where.python.actually.finds.A.when.B.calls.A', autospec=True)
。但我不清楚如何确定是否是这种情况,如果是这样,那么正确的路径是什么。例如,@patch('BDecorated.common.A', autospec=True)
不起作用。
答案 0 :(得分:0)
感谢@user2357112,我找到了这个解决方案。警告:我不知道这是标准还是“最佳”做法,但似乎有效。
首先,将BDecoratedClass
移到test/dummy.py
中自己的文件中。然后将测试更改为:
class TestB(TestCase):
@patch('common.A', autospec=True)
def test_d(self, mock_a):
from test.dummy import BDecoratedClass
b = BDecoratedClass()
b.dummy_func()
mock_a.c.assert_called_once_with() # Succeeds
这会强制修补程序在导入正在修饰的虚拟类之前执行。这有点奇怪,因为导入是在函数内部,但对于一个看起来很好的测试。
更大的警告:
这仅适用于从模块导入内容的第一个测试,在这种情况下BDecoratedClass
导入。在那个时刻,类中的其他所有内容都已加载,无法修补。