首页 文章

使用和不使用pytest-mock模拟标准库函数

提问于
浏览
0

出于测试目的,我想模拟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 .

在我的测试用例中,哪些成功实现模拟的步骤是错误的或错过的?

3 回答

  • 1

    尝试使用monkeypatch . 您可以在示例中看到它们如何"monkeypatch" os.getcwd 返回所需路径 . 在你的情况下,我认为这应该工作:

    monkeypatch.setattr("shutil.which", lambda: "/path/foo.exe")
    
  • 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'
    
  • 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')
    

    补丁装饰器需要函数的 name (完整路径),该函数将从被测系统调用 . mock documentation清楚地解释了这一点 . 以下段落总结了补丁装饰器的这个原理:

    补丁通过(暂时)更改名称指向的对象与另一个对象 . 可以有许多名称指向任何单个对象,因此要修补工作,必须确保修补被测系统使用的名称 .

相关问题