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.