python unittest for argparse

前端 未结 2 2007
心在旅途
心在旅途 2021-01-06 23:31

I have a function inside a module that creates an argparse:

def get_options(prog_version=\'1.0\', prog_usage=\'\', misc_opts=None):
     options         


        
相关标签:
2条回答
  • 2021-01-06 23:55

    I prefer explicitly passing arguments instead of relying on globally available attributes such as sys.argv (which parser.parse_args() does internally). Thus I usually use argparse by passing the list of arguments myself (to main() and subsequently get_options() and wherever you need them):

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

    and then pass in the arguments

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

    that way I can replace and test any list of arguments I like

    options = get_options(['-c','config.yaml'])
    self.assertEquals('config.yaml', options.config) 
    
    0 讨论(0)
  • 2021-01-07 00:11

    Your error message stack is hard to read because it is in quoted form rather than code. But I think the -v argument is producing a sys.exit. version is like help - it's supposed to display a message and then exit. The -v is used by unittest, but is also read by your parser.

    There is an argparse unittest module, test/test_argparse.py. You may need a development Python installation to see that. Some tests are straightforward, others use specialized testing structure. Some of that special code creates arguments in the same way you do with options.

    The are two special issues:

    • generating the input. parse_args uses sys.argv[1:] unless its argv parameter is not None. So you can test a parser by either modifying the sys.argv list (unittest has already used your commandline values), or by passing a argv=None keyword argument into your function and on to parse_args. Trying to make a commandline meant for the unittest code to work with get_options is too complicated.

    • trapping the output, especially the sys.exit generated by errors. One option is to subclass ArgumentParser and give it a different error and/or exit method. Another is to wrap the function call in a try block.

    unittest takes -c argument, but with a different syntax and meaning

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

    and -v is verbose, not version.

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

    This tests the config argument (in a self contained one file form)

    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()
    
    0 讨论(0)
提交回复
热议问题