Здесь есть ряд похожих (иш) вопросов о том, как в 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
и 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()
Другие ответы предлагают переопределение 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()