从测试中的方法中导入的模块中修补方法



我正试图为我的main模块和main()方法编写一个单元测试(如果需要,可以称之为集成测试(。

它看起来像这样:

# main.py in mymodule
# some imports...
def main() -> None:
# args is a result of argparse parsing
try:
if args.type == 'a':
from mymodule import a
a.run()
elif args.type == 'b'
from mymodule import b
b.run()
# elif ...
else:
raise RuntimeError('Unknown type ' + args.type)
except (RuntimeError, FileNotFoundError, ...) as e:
# some logging
sys.exit(1)
# some other logging

我试着用以下方式模拟/修补模块:

def dummy_run():
# just do nothing here
def test_main_a(self):
import mymodule.a
mymodule.a.run = dummy_run
os.system('python3 mymodule.main a')

def test_main_a(self):
# patch is imported as from unittest.mock import patch
with patch('mymodule.a.run', return_value=None):
os.system('python3 mymodule.main a')

def test_main_a(self):
# patch is imported as from unittest.mock import patch
with patch('mymodule.a.run') as run_mocked:
run_mocked.return_value = None
os.system('python3 mymodule.main a')

@patch('mymodule.a.run')
def test_main_a(self, a_mock):
a_mock.return_value = None
os.system('python3 mymodule.main a')

但所有这些方法都不会模拟/修补mymodule.a.run方法,最终会调用实际的方法。

当我尝试修补mymodule.main.a.runmymodule.main.mymodule.a.run时,我只得到了ModuleNotFoundErrors。我在阅读了修补位置部分后尝试了这些目标。

老实说,我真的不明白补丁的问题在哪里,因为我是在模块mymodule.a导入之前进行补丁的,并且它的run()被调用(因为这只发生在测试中的os.system('...')部分之后(。非常感谢您的帮助!

Patching和mocking只在同一个解释器中工作,但对os.system('python [...]')的调用在新的解释器中运行main函数。相反,您可能希望直接调用main函数。这可能涉及修补一些sys对象,如sys.exitsys.argv

然而,一种常见的做法是为main函数提供一个可选的args参数:

def main(args=None):
parser = argparse.ArgumentParser()
parser.add_argument('type')
namespace = parser.parse_args(args)
if namespace.type == 'a':
from mymodule import a
a.run()
sys.exit()

通过这种方式,可以很容易地注入和测试命令行参数:

def test_main_a():
with patch('mymodule.a.run') as run_mocked:
main(['a'])

此外,pytest.raises上下文可用于管理对sys.exit:的调用

def test_main_a():
with patch('mymodule.a.run') as run_mocked:
with pytest.raises(SystemExit):
main(['a'])

最新更新