从__getattr__方法检索的模拟函数

时间:2018-06-22 06:36:48

标签: python python-3.x unit-testing mocking pytest

我正在自动执行一些存储库操作,并且正在为此工作使用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)

我的问题是如何进行测试?我正在将pytestpytest-mock配合使用,它提供了mocker固定装置,这是我的尝试:

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()

它们都可以工作,但是我觉得有更好的方法,也许我的测试方法是错误的。

1 个答案:

答案 0 :(得分:4)

感谢jonrsharpe指导我使用create参数,我设法通过以下代码实现了我想要的:

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

documentation摘录来解释create=True的作用:

  

默认情况下,patch()将无法替换不存在的属性。如果您传入create = True且该属性不存在,则修补程序将在调用已修补的函数时为您创建该属性,然后再将其删除。