2016-06-08 21 views
1

Ich habe eine Funktion in einem Modul, das eine argparse erstellt:Python Unittest für 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() 

Eine Probe myapp.py wäre:

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 

und dies wird als genannt werden:

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

Jetzt versuche ich einen Komponententest für diese Funktion zu erstellen, aber mein Problem ist, dass ich ' t übergeben Befehlszeilenparameter über Testcode.

# 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 

Mein Problem ist, dass ich die Befehlszeilenargumente zu get_options() übergeben müssen, aber ich weiß nicht, wie es richtig zu machen.

Erwartete richtiger Aufruf: (. -c config.yaml sollte irgendwie in dem Test-Code übergeben werden) python mytest.py

Was ist "Arbeit"/jetzt nicht arbeiten:

  1. python mytest.py -c config.yaml ist auch nicht funktioniert. Gibt AttributeError: 'module' object has no attribute 'config' zurück, da erwartet wird, dass ich argParseTestCase stattdessen anrufe. Mit anderen Worten, python mytest.py -c argParseTestCase "funktioniert", würde aber natürlich eine AssertionError: 'config.yaml' != 'argParseTestCase'
  2. python mytest.py -v sein, um den Komponententest im ausführlichen Modus auszuführen, schlägt auch fehl. Es gibt:

    test_parser (main.argParseTestCase) ... mytest.py 1.0 ERROR ERROR: test_parser (main.argParseTestCase)
    Traceback (most recent call last): File "tests/unit_tests/mytest.py", line 376, in test_parser options = get_options() File "/root/test/lib/python2.7/site-packages/mymodule.py", line 61, in get_options return parser.parse_args()
    File "/usr/local/lib/python2.7/argparse.py", line 1701, in parse_args args, argv = self.parse_known_args(args, namespace)
    File "/usr/local/lib/python2.7/argparse.py", line 1733, in parse_known_args namespace, args = self._parse_known_args(args, namespace)
    File "/usr/local/lib/python2.7/argparse.py", line 1939, in _parse_known_args start_index = consume_optional(start_index)
    File "/usr/local/lib/python2.7/argparse.py", line 1879, in consume_optional take_action(action, args, option_string)
    File "/usr/local/lib/python2.7/argparse.py", line 1807, in take_action action(self, namespace, argument_values, option_string)
    File "/usr/local/lib/python2.7/argparse.py", line 1022, in call parser.exit(message=formatter.format_help())
    File "/usr/local/lib/python2.7/argparse.py", line 2362, in exit _sys.exit(status) SystemExit: 0

Antwort

2

Ihre Fehlermeldung Stapel sind schwer zu lesen, weil es in zitierte Form eher als Code. Aber ich denke, das -v Argument produziert eine sys.exit. version ist wie help - es soll eine Nachricht anzeigen und dann beenden. Die -v wird von unittest verwendet, aber auch von Ihrem Parser gelesen.

Es gibt ein argparse Unitest Modul, test/test_argparse.py. Möglicherweise benötigen Sie eine Python-Entwicklungsumgebung, um das zu sehen. Einige Tests sind unkompliziert, andere verwenden spezielle Teststrukturen. Ein Teil dieses speziellen Codes erstellt Argumente auf die gleiche Weise wie mit options.

Das sind zwei Sonderausgaben:

  • den Eingang zu erzeugen. parse_args verwendet sys.argv[1:], es sei denn, sein argv Parameter ist nicht None. Sie können also einen Parser testen, indem Sie entweder die sys.argv Liste ändern (unittest hat Ihre Befehlszeilenwerte bereits verwendet) oder indem Sie ein Schlüsselwort argv=None in Ihre Funktion und an parse_args übergeben. Der Versuch, eine Befehlszeile zu erstellen, die für den unittest-Code gedacht ist, um mit get_options zu arbeiten, ist zu kompliziert.

  • Trapping der Ausgabe, vor allem die sys.exit generiert durch Fehler. Eine Option ist die Unterklasse ArgumentParser und geben Sie eine andere Methode error und/oder exit. Eine weitere Möglichkeit besteht darin, den Funktionsaufruf in einen try Block zu schreiben.

unittest nimmt -c Argument, aber mit einer anderen Syntax und Bedeutung

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

und -v ist verbose, nicht version.

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

Dies testet die config Argument (in einem selbst enthielt eine Datei 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() 
3

ich ausdrücklich Argumente zu übergeben lieber, anstatt sich auf global verfügbaren Attributen wie sys.argv (was parser.parse_args() intern tut). So in der Regel verwende ich argparse durch die Liste der Argumente mich vorbei (auf main() und anschließend get_options() und überall dort, wo Sie sie benötigen):

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

und dann in den Argumenten übergeben

def main(args): 
    get_options(args) 

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

auf diese Weise ich ersetzen und testen Sie eine Liste von Argumenten, die ich mag

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