我正试图为我的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.run
或mymodule.main.mymodule.a.run
时,我只得到了ModuleNotFoundError
s。我在阅读了修补位置部分后尝试了这些目标。
老实说,我真的不明白补丁的问题在哪里,因为我是在模块mymodule.a
导入之前进行补丁的,并且它的run()
被调用(因为这只发生在测试中的os.system('...')
部分之后(。非常感谢您的帮助!
Patching和mocking只在同一个解释器中工作,但对os.system('python [...]')
的调用在新的解释器中运行main
函数。相反,您可能希望直接调用main
函数。这可能涉及修补一些sys
对象,如sys.exit
和sys.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'])