2016-08-18 1 views
4

Ausgehend von Greg Haskin's answer in this question, ich habe versucht, einen Unittest zu machen, um zu überprüfen, dass argparse den entsprechenden Fehler gibt, wenn ich es einige Args übergebe, die nicht in choices vorhanden sind. unittest generiert jedoch einen falschen positiven Wert mit der folgenden Anweisung try/except.Unittest verwenden, um Argparse zu testen - Exit-Fehler

Zusätzlich, wenn ich einen Test unter Verwendung nur einer with assertRaises Anweisung mache, zwingt argparse das System beenden und das Programm führt keine weiteren Tests.

Ich möchte in der Lage sein, einen Test dafür zu haben, aber vielleicht ist es redundant angesichts der Tatsache, dass argparse bei einem Fehler beendet?

#!/usr/bin/env python3 

import argparse 
import unittest 

class sweep_test_case(unittest.TestCase): 
    """Tests that the merParse class works correctly""" 

    def setUp(self): 
     self.parser=argparse.ArgumentParser() 
     self.parser.add_argument(
      "-c", "--color", 
      type=str, 
      choices=["yellow", "blue"], 
      required=True) 

    def test_required_unknown_TE(self): 
     """Try to perform sweep on something that isn't an option. 
     Should return an attribute error if it fails. 
     This test incorrectly shows that the test passed, even though that must 
     not be true.""" 
     args = ["--color", "NADA"] 
     try: 
      self.assertRaises(argparse.ArgumentError, self.parser.parse_args(args)) 
     except SystemExit: 
      print("should give a false positive pass") 

    def test_required_unknown(self): 
     """Try to perform sweep on something that isn't an option. 
     Should return an attribute error if it fails. 
     This test incorrectly shows that the test passed, even though that must 
     not be true.""" 
     args = ["--color", "NADA"] 
     with self.assertRaises(argparse.ArgumentError): 
      self.parser.parse_args(args) 

if __name__ == '__main__': 
    unittest.main() 

Fehler:

Usage: temp.py [-h] -c {yellow,blue} 
temp.py: error: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue') 
E 
usage: temp.py [-h] -c {yellow,blue} 
temp.py: error: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue') 
should give a false positive pass 
. 
====================================================================== 
ERROR: test_required_unknown (__main__.sweep_test_case) 
Try to perform sweep on something that isn't an option. 
---------------------------------------------------------------------- 
Traceback (most recent call last): #(I deleted some lines) 
    File "/Users/darrin/anaconda/lib/python3.5/argparse.py", line 2310, in _check_value 
    raise ArgumentError(action, msg % args) 
argparse.ArgumentError: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue') 

During handling of the above exception, another exception occurred: 

Traceback (most recent call last): #(I deleted some lines) 
    File "/anaconda/lib/python3.5/argparse.py", line 2372, in exit 
    _sys.exit(status) 
SystemExit: 2 
+0

Die Testdatei 'test/test_argparse.py' hat eine Fülle von Beispielen, da sie die meisten Funktionen des Moduls testet. Das 'sys.exit' benötigt eine spezielle Handhabung. – hpaulj

+0

Danke @hpaulj, wo kann ich diese Datei auf meinem System finden? [Ich habe herausgefunden, worüber ich hier spreche] (https://hg.python.org/cpython/file/default/Lib/test/test_argparse.py). – conchoecia

+0

Ja, das ist die Datei. Möglicherweise benötigen Sie eine Entwicklungsversion von Python, um sie auf Ihrem eigenen Computer zu finden. Suchen Sie nach dem Verzeichnis 'Lib/test'.Aber der Download aus dem Repository ist auch in Ordnung. Die meisten Tests, die auf 'ParserTestCase' aufbauen, kümmern sich nicht um die Fehlermeldung; nur ob der Fall läuft oder nicht. Testen Sie weiter unten in der Datei nach Fehlermeldungen. – hpaulj

Antwort

3

Während der Parser bei der Analyse ein bestimmtes Argument ein Argument erhöhen kann, dass in der Regel gefangen und zu parser.error und parse.exit weitergegeben. Das Ergebnis ist, dass die Verwendung zusammen mit einer Fehlermeldung und dann sys.exit(2) gedruckt wird.

So asssertRaises ist keine gute Möglichkeit für diese Art von Fehler in argparse zu testen. Die Unitest-Datei für das Modul, test/test_argparse.py hat eine aufwendige Art und Weise um dies zu umgehen, die Unterklassen der ArgumentParser, neu definiert seine error Methode, und Umleitung der Ausgabe.

parser.parse_known_args (die durch parse_args genannt wird) endet mit:

try: 
     namespace, args = self._parse_known_args(args, namespace) 
     if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): 
      args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) 
      delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) 
     return namespace, args 
    except ArgumentError: 
     err = _sys.exc_info()[1] 
     self.error(str(err)) 

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

Wie diesen Test (I‘

import argparse 
import unittest 

class ErrorRaisingArgumentParser(argparse.ArgumentParser): 
    def error(self, message): 
     #print(message) 
     raise ValueError(message) # reraise an error 

class sweep_test_case(unittest.TestCase): 
    """Tests that the Parse class works correctly""" 

    def setUp(self): 
     self.parser=ErrorRaisingArgumentParser() 
     self.parser.add_argument(
      "-c", "--color", 
      type=str, 
      choices=["yellow", "blue"], 
      required=True) 

    def test_required_unknown(self): 
     """Try to perform sweep on something that isn't an option. 
     Should pass""" 
     args = ["--color", "NADA"] 
     with self.assertRaises(ValueError) as cm: 
      self.parser.parse_args(args) 
     print('msg:',cm.exception) 
     self.assertIn('invalid choice', str(cm.exception)) 

if __name__ == '__main__': 
    unittest.main() 

mit einem Lauf: mehrere Ideen von test_argparse.py ve entlehnt

1931:~/mypy$ python3 stack39028204.py 
msg: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue') 
. 
---------------------------------------------------------------------- 
Ran 1 test in 0.002s 

OK 
+0

Danke, das ist sehr hilfreich – conchoecia

1

Wenn Sie im Fehlerprotokoll anschaut, kann man sehen, dass ein argparse.ArgumentError angehoben und keine AttributeError. Ihr Code sollte wie folgt aussehen:

#!/usr/bin/env python3 

import argparse 
import unittest 
from argparse import ArgumentError 

class sweep_test_case(unittest.TestCase): 
    """Tests that the merParse class works correctly""" 

    def setUp(self): 
     self.parser=argparse.ArgumentParser() 
     self.parser.add_argument(
      "-c", "--color", 
      type=str, 
      choices=["yellow", "blue"], 
      required=True) 

    def test_required_unknown_TE(self): 
     """Try to perform sweep on something that isn't an option. 
     Should return an attribute error if it fails. 
     This test incorrectly shows that the test passed, even though that must 
     not be true.""" 
     args = ["--color", "NADA"] 
     try: 
      self.assertRaises(ArgumentError, self.parser.parse_args(args)) 
     except SystemExit: 
      print("should give a false positive pass") 

    def test_required_unknown(self): 
     """Try to perform sweep on something that isn't an option. 
     Should return an attribute error if it fails. 
     This test incorrectly shows that the test passed, even though that must 
     not be true.""" 
     args = ["--color", "NADA"] 
     with self.assertRaises(ArgumentError): 
      self.parser.parse_args(args) 

if __name__ == '__main__': 
    unittest.main() 
+0

Danke für den Vorschlag. Wenn ich "AttributeError" durch "ArgumentError" ersetze, wie Sie vorgeschlagen haben, bekomme ich 'NameError: name 'ArgumentError' ist nicht definiert'. Dies ist sinnvoll, da ArgumentError nicht im allgemeinen Namensraum ist, sondern Teil von 'argparse'. Ich habe dann versucht, 'AttributeError' durch' argparse.ArgumentError' zu ersetzen und habe die gleichen Fehler wie oben. Ich habe meine Frage bearbeitet, um dies zu reflektieren. – conchoecia

1

Wenn Sie in den Quellcode von argparse, in argparse.py, um Zeile 1732 (meine Python-Version ist 3.5.1) schauen, gibt es eine Methode ArgumentParser namens parse_known_args. Der Code ist:

# parse the arguments and exit if there are any errors 
try: 
    namespace, args = self._parse_known_args(args, namespace) 
    if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): 
     args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) 
     delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) 
    return namespace, args 
except ArgumentError: 
    err = _sys.exc_info()[1] 
    self.error(str(err)) 

So wird die ArgumentError von argparse geschluckt werden, und Ausgang mit einem Fehlercode. Wenn Sie das trotzdem testen wollen, ist der einzige Weg, über den ich nachdenken könnte, sys.exc_info.

Verwandte Themen