这里有许多关于如何在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()
这不起作用:当实例化
Quux
时,超类对something
和mixin
的定义没有被模拟,这并不令人意外,因为类的继承是在补丁之前定义的。至少我可以通过显式设置
mixin
来解决mixin
问题。# This works to mock the mixin method
q = Quux()
setattr(q, "mixin", mock_bar.mixin)
然而,类似的方法并不能用于被覆盖的方法something
。
就像我之前提到的,其他回答这个问题的人建议使用模拟对象来覆盖Quux
的__bases__
值。然而,这根本行不通,因为__bases__
必须是一组类的元组,而模拟对象的类似乎只是原始类的副本:
# This doesn't do what I want
Quux.__bases__ = (mock_foo.__class__, mock_bar.__class__)
q = Quux()
其他回答建议覆盖super
。这样做确实有效,但我觉得有点危险,因为你不想打补丁的任何对super
的调用可能会破坏程序的正常运行。
那么除了这种方法,有没有更好的办法来实现我的需求呢?
with patch("builtins.super") as mock_super:
mock_foo = MagicMock(spec=Foo)
mock_foo.something.return_value = 123
mock_super.return_value = mock_foo
mock_bar = MagicMock(spec=Bar)
q = Quux()
setattr(q, "mixin", mock_bar.mixin)
assert q.something(0) == 125
mock_bar.mixin.assert_called_once()