Исправление методов суперкласса с помощью макетов - PullRequest
0 голосов
/ 31 мая 2018

Здесь есть ряд похожих (иш) вопросов о том, как в 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()
...