【问题标题】:python unittest for argparse用于argparse的python单元测试
【发布时间】:2016-10-08 10:14:28
【问题描述】:

我在一个模块中有一个函数,它创建了一个argparse

def get_options(prog_version='1.0', prog_usage='', misc_opts=None):
     options = [] if misc_opts is None else misc_opts
     parser = ArgumentParser(usage=prog_usage) if prog_usage else ArgumentParser()
     parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(prog_version))
     parser.add_argument('-c', '--config', dest='config', required=True, help='the path to the configuration file')

    for option in options:
        if 'option' in option and 'destination' in option:
            parser.add_argument(option['option'],
                                dest=option.get('destination', ''),
                                default=option.get('default', ''),
                                help=option.get('description', ''),
                                action=option.get('action', 'store'))

    return parser.parse_args()

myapp.py 的示例将是:

my_options = [
    {
        "option": "-s",
        "destination": "remote_host",
        "default": "127.0.0.1",
        "description": "The remote server name or IP address",
        "action": "store"
    },
]

# Get Command Line Options
options = get_options(misc_opts=my_options)
print options.config
print options.remote_host

这将被称为:

$> python myapp.py -c config.yaml
$> config.yaml
   127.0.0.1

现在,我正在尝试为此函数创建一个单元测试,但我的问题是我无法通过测试代码传递命令行参数。

# mytest.py
import unittest
from mymodule import get_options

class argParseTestCase(unittest.TestCase):
     def test_parser(self):
         options = get_options()
         # ...pass the command line arguments...
         self.assertEquals('config.yaml', options.config) # ofcourse this fails because I don't know how I will pass the command line arguments

我的问题是我需要将命令行参数传递给get_options(),但我不知道如何正确执行。

预期的正确调用:python mytest.py-c config.yaml 应该以某种方式在测试代码中传递。)

什么是“工作”/现在不工作:

  1. python mytest.py -c config.yaml 也不起作用。返回AttributeError: 'module' object has no attribute 'config',因为它希望我改为调用argParseTestCase。换句话说,python mytest.py -c argParseTestCase“有效”,但当然是回报AssertionError: 'config.yaml' != 'argParseTestCase'
  2. python mytest.py -v 在详细模式下运行单元测试也会失败。它返回:

    test_parser (ma​​in.argParseTestCase) ... mytest.py 1.0 错误 错误:test_parser (ma​​in.argParseTestCase)
    回溯(最近一次通话最后): 文件“tests/unit_tests/mytest.py”,第 376 行,在 test_parser options = get_options() 文件“/root/test/lib/python2.7/site-packages/mymodule.py”,第 61 行,在 get_options 返回 parser.parse_args()
    文件“/usr/local/lib/python2.7/argparse.py”,第 1701 行,在 parse_args args 中,argv = self.parse_known_args(args, namespace)
    文件“/usr/local/lib/python2.7/argparse.py”,第 1733 行,在 parse_known_args 命名空间中,args = self._parse_known_args(args, namespace)
    文件“/usr/local/lib/python2.7/argparse.py”,第 1939 行,在 _parse_known_args start_index = consume_optional(start_index)
    文件“/usr/local/lib/python2.7/argparse.py”,第 1879 行,在 consume_optional take_action(action, args, option_string)
    文件“/usr/local/lib/python2.7/argparse.py”,第 1807 行,在 take_action 操作中(self、namespace、argument_values、option_string)
    调用中的文件“/usr/local/lib/python2.7/argparse.py”第 1022 行 parser.exit(message=formatter.format_help())
    文件“/usr/local/lib/python2.7/argparse.py”,第 2362 行,退出 _sys.exit(status) 系统退出:0

【问题讨论】:

    标签: python python-2.7 unit-testing argparse


    【解决方案1】:

    我更喜欢显式传递参数,而不是依赖全局可用的属性,例如 sys.argvparser.parse_args() 在内部执行)。因此,我通常通过自己传递参数列表来使用argparse(传递给main(),随后传递给get_options(),以及你需要它们的任何地方):

    def get_options(args, prog_version='1.0', prog_usage='', misc_opts=None):
        # ...
        return parser.parse_args(args)
    

    然后传入参数

    def main(args):
        get_options(args)
    
    if __name__ == "__main__":
        main(sys.argv[1:])
    

    这样我可以替换和测试我喜欢的任何参数列表

    options = get_options(['-c','config.yaml'])
    self.assertEquals('config.yaml', options.config) 
    

    【讨论】:

      【解决方案2】:

      您的错误消息堆栈很难阅读,因为它是引用形式而不是代码。但我认为-v 参数正在产生sys.exitversion 就像help - 它应该显示一条消息然后退出。 -vunittest 使用,但也被你的解析器读取。

      有一个argparse 单元测试模块test/test_argparse.py。您可能需要安装开发 Python 才能看到这一点。有些测试很简单,有些则使用专门的测试结构。其中一些特殊代码创建参数的方式与使用 options 的方式相同。

      有两个特殊问题:

      • 生成输入。 parse_args 使用 sys.argv[1:],除非它的 argv 参数不是 None。因此,您可以通过修改sys.argv 列表(unittest 已使用您的命令行值)或将argv=None 关键字参数传递给您的函数并传递给parse_args 来测试解析器。尝试为 unittest 代码创建一个命令行以使用 get_options 太复杂了。

      • 捕获输出,尤其是错误生成的sys.exit。一种选择是继承ArgumentParser 并给它一个不同的error 和/或exit 方法。另一种是将函数调用包装在try 块中。

      unittest 采用 -c 参数,但语法和含义不同

       -c, --catch      Catch control-C and display results
      

      -vverbose,而不是version

      =============

      这会测试config 参数(在一个自包含的文件形式中)

      import unittest
      import sys
      #from mymodule import get_options
      
      def get_options(argv=None, prog_version='1.0', prog_usage='', misc_opts=None):
          # argv is optional test list; uses sys.argv[1:] is not provided
          from argparse import ArgumentParser
          options = [] if misc_opts is None else misc_opts
          parser = ArgumentParser(usage=prog_usage) if prog_usage else ArgumentParser()
          parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(prog_version))
          parser.add_argument('-c', '--config', dest='config', help='the path to the configuration file')
      
          for option in options:
              if 'option' in option and 'destination' in option:
                  parser.add_argument(option['option'],
                                      dest=option.get('destination', ''),
                                      default=option.get('default', ''),
                                      help=option.get('description', ''),
                                      action=option.get('action', 'store'))
      
          args = parser.parse_args(argv)
          print('args',args)
          return args
      
      class argParseTestCase(unittest.TestCase):
           def test_config(self):
               sys.argv[1:]=['-c','config.yaml']       
               options = get_options()
               self.assertEquals('config.yaml', options.config) 
           def test_version(self):
               sys.argv[1:]=['-v']   
               with self.assertRaises(SystemExit):
                          get_options() 
               # testing version message requires redirecting stdout
           # similarly for a misc_opts test
      
      if __name__=='__main__':
          unittest.main()
      

      【讨论】:

        猜你喜欢
        • 2018-09-25
        • 2017-07-08
        • 2021-12-09
        • 2016-09-20
        • 2019-11-15
        • 1970-01-01
        • 2021-03-22
        • 1970-01-01
        • 2013-08-05
        相关资源
        最近更新 更多