Насмешливые функции, полученные из метода __getattr__

1

Я автоматизирую некоторые операции репозитория, и я использую GitPython для этой работы. Позвольте упростить вещи и предположим, что я хотел бы утверждать, называется ли моя функция методом pull в репозитории. Код ниже:

from pytest_mock import MockFixture
from git import Git, Repo

repo = Repo('/Users/Jatimir/path/to/repo')

def pull() -> None:
    repo.git.pull()

Тем не менее, я заметил, что класс Git несколько особенный и не реализует pull. Вместо этого он "делегирует" весь трафик на __getattr__ который использует другой метод, который выполняет задание.

def __getattr__(self, name):
    ...
    return lambda *args, **kwargs: self._call_process(name, *args, **kwargs)

Мой вопрос: как подойти к тестированию? Я использую pytest с pytest-mock, который обеспечивает mocker fixture, и вот мои попытки:

def test_pull1(mocker: MockFixture) -> None:
    pull_mock = mocker.MagicMock(name='pull')
    getattr_mock = mocker.MagicMock(name='__getattr__', return_value=pull_mock)

    mocker.patch.object(Git, '__getattr__', getattr_mock)
    pull()
    pull_mock.assert_called_once_with()


def test_pull2(mocker: MockFixture) -> None:
    pull_mock = mocker.Mock(name='pull')

    def __getattr__(self, name):
        if name == 'pull':
            return pull_mock

    mocker.patch.object(Git, '__getattr__', __getattr__)
    pull()
    pull_mock.assert_called_once_with()

Они оба работают, но я чувствую, что есть лучший способ, и, может быть, мой подход к тестированию - это неправильно.

Теги:
python-3.x
unit-testing
mocking
pytest

1 ответ

1
Лучший ответ

Благодаря jonrsharpe, который инструктировал меня использовать аргумент create мне удалось добиться того, что я хотел, со следующим кодом:

def test_pull(mocker: MockFixture) -> None:
    m = mocker.patch.object(Git, 'pull', create=True)
    pull()
    m.assert_called_once_with()

Выдержка из документации, объясняющей, что create=True:

По умолчанию patch() не сможет заменить атрибуты, которые не существуют. Если вы передадите create = True, и атрибут не существует, патч создаст для вас атрибут при вызове исправленной функции и снова удалит его.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню