使用 unittest 发出模拟 argparse

Issue mocking argparse with unittest

我有需要测试的客户端代码

def arg_parser(arg_parse=None):
    if not arg_parse:
        arg_parse = sys.argv[1:]
    parser = argparse.ArgumentParser()
    parser.add_argument('--a', type=str, required=True)
    parser.add_argument('--b', type=str, required=True)
    return parser.parse_args(arg_parse)

def main():
      arg_parse = arg_parser()
      update_config(arg_parse)
      ......

def update_config(conf_args: argparse):
    # read default YAML configuration file
    with open('resources/config.yaml', 'r') as file:
        conf = yaml.safe_load(file.read())
    # Updated the YAML configuration file
    if conf_args.a: // gives error here
        ..........

if __name__ == '__main__':
    main()

我正在通过模拟 arg_parser 方法来测试这个主要方法

@mock.patch('test.prepare.arg_parser')
def test_main(mock_arg_parser):
    args = arg_parser(['--a', 'a1',
                       '--b', 'b1'])

    mock_arg_parser.return_value = args
    ...
    main()

但是当我尝试从 argparse (arg_parse.a)

访问参数时出现异常
        # read default YAML configuration file
        with open('resources/config.yaml', 'r') as file:
            conf = yaml.safe_load(file.read())
        # Updated the YAML configuration file
>       if conf_args.a:
          AttributeError: 'list' object has no attribute 'a'

不幸的是,我无法对齐客户端代码,因此需要修复测试。

所以事实证明您根本不需要涉及模拟——您可以按如下方式简化代码:

def arg_parser(argv):
    parser = argparse.ArgumentParser()
    parser.add_argument('--a', type=str, required=True)
    parser.add_argument('--b', type=str, required=True)
    return parser.parse_args(arg_parse)

def main(argv: Sequence[str] | None = None):
      arg_parse = arg_parser(argv)
      update_config(arg_parse)
      ......

def update_config(conf_args: argparse):
    # read default YAML configuration file
    with open('resources/config.yaml', 'r') as file:
        conf = yaml.safe_load(file.read())
    # Updated the YAML configuration file
    if conf_args.a: # gives error here
        ..........

if __name__ == '__main__':
    main()

然后在你的测试中显式地传递参数列表:

def test_main():
    main(['--a', 'a1', '--b', 'b1'])

无需复杂的模拟!

这是可行的,因为 argparse.ArgumentParser.parse_args 在传递 None 时默认为 sys.argv[1:](默认情况下在您的脚本中发生),但您可以为测试传递测试参数!我复习了这个技巧 in a video I made