用mocks修补超类方法

时间:2018-05-31 14:05:56

标签: python-3.x unit-testing mocking patch superclass

这里有很多类似的(ish)问题,关于如何在Python中修补类的超类,以便进行测试。我从他们那里收集了一些想法,但我还不是我需要的地方。

想象一下,我有两个基类:

class Foo(object):
    def something(self, a):
        return a + 1

class Bar(object):
    def mixin(self):
        print("Hello!")

现在我定义了我想要测试的类:

class Quux(Foo, Bar):
    def something(self, a):
        self.mixin()
        return super().something(a) + 2

说我想测试mixin已被调用,我想替换被模拟的Foo.something的返回值,但重要的是(并且必然)我不想更改任何Quux.something中的控制流程或逻辑。假设修补超级课程"刚刚工作",我尝试了unittest.mock.patch

with patch("__main__.Foo", spec=True) as mock_foo:
    with patch("__main__.Bar", spec=True) as mock_bar:
        mock_foo.something.return_value = 123
        q = Quux()
        assert q.something(0) == 125
        mock_bar.mixin.assert_called_once()

这不起作用:超级班级'当something被实例化时,mixinQuux的定义不会被模拟,这并不奇怪,因为班级'}继承是在补丁之前定义的。

至少可以通过明确设置来解决mixin问题:

# This works to mock the mixin method
q = Quux()
setattr(q, "mixin", mock_bar.mixin)

但是,类似的方法对于重写方法something不起作用。

正如我所提到的,这个问题的其他答案建议用模拟覆盖Quux的{​​{1}}值。但是,这根本不起作用,因为__bases__必须是一个类的元组和模拟'课程似乎只是原作:

__bases__

其他答案建议覆盖# This doesn't do what I want Quux.__bases__ = (mock_foo.__class__, mock_bar.__class__) q = Quux() 。这个确实有效,但我觉得它有点危险,因为任何你想要补丁的super的调用可能会破坏可怕的事情。

那么有更好的方法来做我想要的事情吗?

super

1 个答案:

答案 0 :(得分:0)

问题实际上很简单- 子类将包含对原始类的引用 内部结构(公共可见属性__bases____mro__)。模拟那些基类时,该引用不会更改- 当补丁被“打开”时,模拟只会显式地影响使用这些对象的对象。换句话说,仅当您的Quux类本身在with块内定义时,才使用它们。而且这也不起作用,因为替换类的“模拟”对象不能是正确的超类。

但是,解决方法和正确的实现方法非常简单-您只需模拟要替换的方法,而不是类。

这个问题现在有点老了,我希望你继续前进,但是正确的做法是:

with patch("__main__.Foo.something", spec=True) as mock_foo:
    with patch("__main__.Bar.mixin", spec=True) as mock_bar:
        mock_foo.return_value = 123
        q = Quux()
        assert q.something(0) == 125
        mock_bar.assert_called_once()