出于测试目的,我想模拟shutil.which(Python 3.5.1),它在一个简化方法里面调用find_foo()
def _find_foo(self) -> Path:
foo_exe = which('foo', path=None)
if foo_exe:
return Path(foo_exe)
else:
return None
我正在使用pytest来实现我的测试用例。因此,我也想使用pytest扩展pytest-mock。在下面我使用pytest + pytest-mock粘贴了一个示例测试用例:
def test_find_foo(mocker):
mocker.patch('shutil.which', return_value = '/path/foo.exe')
foo_path = find_foo()
assert foo_path is '/path/foo.exe'
这种使用pytest-mock进行模拟的方法不起作用。仍然调用shutil.which而不是模拟。
我试图直接使用mock包,它现在是Python3的一部分:
def test_find_foo():
with unittest.mock.patch('shutil.which') as patched_which:
patched_which.return_value = '/path/foo.exe'
foo_path = find_foo()
assert foo_path is '/path/foo.exe'
可悲的是结果是一样的。同样调用shutil.which()
而不是指定mock。
在我的测试用例中,成功实现模拟的哪些步骤是错误的或错过的?
答案 0 :(得分:1)
尝试使用monkeypatch。你可以在例子中看到他们" monkeypatch" os.getcwd
返回想要的路径。在你的情况下,我认为这应该有效:
monkeypatch.setattr("shutil.which", lambda: "/path/foo.exe")
答案 1 :(得分:1)
将which
方法注入您的方法或对象将允许您在没有pytest-mock
的情况下模拟依赖项。
def _find_foo(self, which_fn=shutil.which) -> Path:
foo_exe = which_fn('foo', path=None)
if foo_exe:
return Path(foo_exe)
else:
return None
def test_find_foo():
mock_which = Mock(return_value = '/path/foo.exe')
foo_path = obj._find_foo(which_fn=mock_which)
assert foo_path is '/path/foo.exe'
答案 2 :(得分:1)
我研究了更多时间学习unittest.mock和pytest-mock。我找到了一个简单的解决方案,而没有使用补丁装饰器修改生产代码。在下面我粘贴了一个代码片段,演示了使用pytest-mock的第三种方法:
def test_find_foo(mocker):
mocker.patch('__main__.which', return_value='/path/foo.exe')
foo_path = find_foo()
assert foo_path == Path('/path/foo.exe')
没有pytest-mock(plain unittest-mock和@patch装饰器),这个解决方案也可以工作。上面代码段中的重要一行是
mocker.patch('__main__.which', return_value='/path/foo.exe')
补丁装饰器需要从被测系统调用的函数的名称(完整路径)。 mock documentation清楚地解释了这一点。以下段落总结了补丁装饰器的这个原理:
补丁通过(暂时)更改名称指向的对象与另一个对象。可以有许多名称指向任何单个对象,因此要修补工作,您必须确保修补被测系统使用的名称。