15 Stimmen

Wie spiele ich die Python-Methode OptionParser.error() nach, die ein sys.exit() ausführt?

Ich versuche, einen Code zu testen, der wie folgt aussieht:

def main():
    parser = optparse.OptionParser(description='This tool is cool', prog='cool-tool')
    parser.add_option('--foo', action='store', help='The foo option is self-explanatory')
    options, arguments = parser.parse_args()
    if not options.foo:
        parser.error('--foo option is required')
    print "Your foo is %s." % options.foo
    return 0

if __name__ == '__main__':
   sys.exit(main())

Mit einem Code, der wie folgt aussieht:

@patch('optparse.OptionParser')
def test_main_with_missing_p4clientsdir_option(self, mock_optionparser):
    #
    # setup
    #
    optionparser_mock = Mock()
    mock_optionparser.return_value = optionparser_mock
    options_stub = Mock()
    options_stub.foo = None
    optionparser_mock.parse_args.return_value = (options_stub, sentinel.arguments)
    def parser_error_mock(message):
        self.assertEquals(message, '--foo option is required')
        sys.exit(2)
    optionparser_mock.error = parser_error_mock

    #
    # exercise & verify
    #
    self.assertEquals(sut.main(), 2)

Ich benutze Michael Foord's Mock und die Nase, um die Tests durchzuführen.

Wenn ich den Test ausführe, erhalte ich:

  File "/Users/dspitzer/Programming/Python/test-optparse-error/tests/sut_tests.py", line 27, in parser_error_mock
    sys.exit(2)
SystemExit: 2

----------------------------------------------------------------------
Ran 1 test in 0.012s

FAILED (errors=1)

Das Problem ist, dass OptionParser.error ein sys.exit(2) ausführt, und main() sich natürlich darauf verlässt. Aber nose oder unittest erkennt den (erwarteten) sys.exit(2) und schlägt den Test fehl.

Ich kann den Test durch Hinzufügen von "return 2" unter dem parser.error()-Aufruf in main() und Entfernen des sys.exit()-Aufrufs aus parser_error_mock() bestehen lassen, aber ich finde es geschmacklos, den zu testenden Code zu ändern, um einen Test bestehen zu lassen. Gibt es eine bessere Lösung?

Update : df Antwort funktioniert, obwohl der richtige Aufruf "self.assertRaises(SystemExit, sut.main)" lautet.

Das bedeutet, dass der Test bestanden wird, egal welche Zahl in sys.exit() in parser_error_mock() steht. Gibt es eine Möglichkeit, auf den Exit-Code zu testen?

BTW, der Test ist robuster, wenn ich hinzufügen:

self.assertEquals(optionparser_mock.method_calls, [('add_option', ('--foo',), {'action': 'store', 'help': 'The foo option is self-explanatory'}), ('parse_args', (), {})])

am Ende.

Aktualisierung 2 : Ich kann auf den Exit-Code testen, indem ich "self.assertRaises(SystemExit, sut.main)" durch ersetze:

try:
    sut.main()
except SystemExit, e:
    self.assertEquals(type(e), type(SystemExit()))
    self.assertEquals(e.code, 2)
except Exception, e:
    self.fail('unexpected exception: %s' % e)
else:
    self.fail('SystemExit exception expected')

2 Stimmen

Das erste assertEquals in Ihrem Update 2 ist unnötig, da die "except"-Zeile darüber nur SystemExit-Ausnahmen abfängt.

12voto

dF. Punkte 70587

Funktioniert das anstelle von assertEquals ?

self.assertRaises(SystemExit, sut.main, 2)

Dies sollte die SystemExit Ausnahme und verhindern, dass das Skript beendet wird.

1voto

Daryl Spitzer Punkte 131841

Wie in meinen Aktualisierungen zu meiner Frage erwähnt, musste ich Folgendes ändern dF ist die Antwort auf:

self.assertRaises(SystemExit, sut.main)

...und ich habe mir ein paar längere Schnipsel ausgedacht, um den Exit-Code zu testen.

[Anmerkung: Ich habe meine eigene Antwort akzeptiert, aber ich werde diese Antwort löschen und akzeptieren dF wenn er seine aktualisiert].

0voto

guerda Punkte 22205

Wahrscheinlich enthält diese Frage einige neue Informationen:

Java: Wie testet man Methoden, die System.exit() aufrufen?

0voto

imolit Punkte 6752

Hinzufügen @raises zu Ihrer Testfunktion. D.h:

from nose.tools import raises

@raises(SystemExit)
@patch('optparse.OptionParser')
def test_main_with_missing_p4clientsdir_option(self, mock_optionparser):
    # Setup
    ...
    sut.main()

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X