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

时间:2018-10-29 11:58:24

标签: python python-3.x unit-testing

我正在尝试为我的 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时,我只有ModuleNotFoundError个。阅读Where to patch部分后,我尝试了这些目标。

老实说,我真的不了解修补程序的问题所在,因为我是在导入模块mymodule.a并调用run()之前进行修补的(因为这仅在{{1}之后}部分)。非常感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

修补和模拟仅在同一解释器中起作用,但是您对os.system('python [...]')的调用将在新解释器中运行main函数。相反,您可能想直接调用main函数。这可能涉及修补某些sys对象,例如sys.exitsys.argv

但是,通常的做法是为args函数提供可选的main参数:

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'])