parser argumentparser arg_parser add_argument python unit-testing mocking optparse

argumentparser - arguments parser python



¿Cómo me burlo del método de Python OptionParser.error(), que hace un sys.exit()? (4)

Estoy tratando de probar un código de la unidad que se ve así:

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())

Con un código que se ve así:

@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)

Estoy usando Mock de Michael Foord , y nariz para ejecutar las pruebas.

Cuando realizo la prueba, obtengo:

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)

El problema es que OptionParser.error hace un sys.exit (2), y así que main () naturalmente depende de eso. Pero nosetest o unittest detecta el (esperado) sys.exit (2) y falla la prueba.

Puedo pasar la prueba agregando "return 2" en la llamada a parser.error () en main () y eliminando la llamada a sys.exit () de parser_error_mock (), pero me parece desagradable modificar el código bajo prueba para permitir que pase una prueba ¿Hay una mejor solución?

Actualización : la respuesta de df funciona, aunque la llamada correcta es "self.assertRaises (SystemExit, sut.main)".

Lo que significa que la prueba pasa el número que sea en sys.exit () en parser_error_mock (). ¿Hay alguna manera de probar el código de salida?

Por cierto, la prueba es más robusta si agrego:

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

al final.

Actualización 2 : puedo probar el código de salida reemplazando "self.assertRaises (SystemExit, sut.main)" con:

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'')


¿Esto funcionará en lugar de assertEquals ?

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

Esto debería capturar la excepción SystemExit y evitar que la secuencia de comandos finalice.


Agregue @raises a su función de prueba. Es decir:

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


Como mencioné en mis actualizaciones a mi pregunta, tuve que modificar la respuesta de dF a:

self.assertRaises(SystemExit, sut.main)

... y se me ocurrió un fragmento más largo para probar el código de salida.

[Nota: acepté mi propia respuesta, pero borraré esta respuesta y aceptaré la de dF si actualiza la suya.]