使用pytest模拟导入模块中包含的函数的行为

Mocking behavior of functions contained in imported module using pytest

假设我有两个文件 main.pytest_main.pymain.py的内容是:

import os

print('script running', os.getcwd())

test_main.py的内容是:

import pytest
import main

运行 pytest -s returns 以下内容:

================================================= test session starts ==================================================
platform linux -- Python 3.8.6, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
rootdir: /home/example_mocking
plugins: dash-1.20.0, anyio-2.2.0
collecting ... script running /home/example_mocking
collected 0 items

================================================ no tests ran in 0.01s =================================================

现在假设 main.py 已经由其他人提供给我,在我实现完整的测试覆盖率之前我不希望修改它。但是,我想修改 os.getcwd() 的行为,使其 returns “Hello” 而不是我当前的工作目录。

有没有一种方法可以在不修改 main.py 的情况下模拟 os 的行为,这样当我 运行 pytest -s 以下显示时?

================================================= test session starts ==================================================
platform linux -- Python 3.8.6, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
rootdir: /home/example_mocking
plugins: dash-1.20.0, anyio-2.2.0
collecting ... script running Hello
collected 0 items

================================================ no tests ran in 0.01s =================================================

我建议重构您的脚本,使其不会对导入产生副作用——这些很难正确地修补和测试而不造成污染。

这是一个调整后的设置,它仍然有效,但更易于测试(您可以在我在这里整理的视频中找到有关此技术的更多信息:python cli tested with pytest

# main.py
import os

def main() -> int:
    print('script running', os.getcwd())
    return 0
    

if __name__ == '__main__':
    raise SystemExit(main())
# main_test.py
import os
from unittest import mock

import main


def test_main_prints_cwd(capsys):
    # can adjust what `getcwd` returns
    with mock.patch.object(os, 'getcwd', return_value='Hello'):
        assert main.main() == 0  # can test the return value of your program
    # can test what your program prints
    out, err = capsys.readouterr()
    assert out == 'script running Hello\n'
    assert err == ''