这里有许多类似的问题,涉及如何在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
说我想测试是否已调用[C0],并且要替换模拟的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
和mixin
的超类定义,这并不奇怪,因为在修补程序之前定义了类的继承。
至少可以通过显式设置它来解决Quux
问题:
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()
。 does可行,但是我觉得这有点危险,因为任何您不想打补丁的对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()
和__bases__
)。模拟那些基类时,该引用不会更改-模拟只会显式影响使用这些对象的对象,而补丁是“打开”的。换句话说,仅当__mro__
类本身在Quux
块内定义时,才使用它们。而且这也不起作用,因为替换类的“模拟”对象不能是适当的超类。
但是,解决方法和正确的方法很简单-您只需要模拟要替换的the methods,而不是类。
这个问题现在有点老了,希望您继续前进,但是正确的做法是:
with