如何在与测试中的类相同的文件中修补类,该类在测试开始之前初始化?

时间:2017-04-07 23:55:16

标签: python python-3.x python-unittest python-decorators

(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)

我希望在我的单元测试中@patchA来隔离并检查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)不起作用。

1 个答案:

答案 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导入。在那个时刻,类中的其他所有内容都已加载,无法修补。