2009-01-08 17 views
14

estoy tratando de probar la unidad un código que se parece a esto:¿Cómo me burlo del método de Python OptionParser.error(), que hace un sys.exit()?

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 el código que se parece a esto:

@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 Michael Foord's Mock, y la nariz para ejecutar las pruebas.

Cuando ejecuto la prueba, me sale:

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 hace un OptionParser.error sys.exit (2), y así main() de forma natural se basa en eso. Pero nosetest o unittest detecta el (esperado) sys.exit (2) y falla la prueba.

Puedo hacer que la prueba pase 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 la código bajo prueba para permitir que pase una prueba. ¿Hay una mejor solución?

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

Lo que significa que la prueba pasa cualquiera que sea el número en el 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 añado:

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 mediante la sustitución de "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') 
+2

La primera assertEquals en su Actualización 2 es innecesaria, ya que la línea "excepto" que se encuentra arriba solo capturará las excepciones de SystemExit. – rbp

Respuesta

1

Como se señaló en mis cambios a mi pregunta, tuve que modificar la respuesta dF 's a:

self.assertRaises(SystemExit, sut.main) 

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

[Nota: He aceptado mi propia respuesta, pero voy a eliminar esta respuesta y aceptar dF 's si se actualiza la de él.]

12

¿Este trabajo en lugar de assertEquals?

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

Esto debería detectar la excepción SystemExit y prevenir el guión de terminación.

+0

Brillante! Gracias. Pero mira mi actualización a la pregunta. –

+0

No he aceptado tu respuesta, ya que no es del todo correcto. Pero dado que me diste la información que me llevó a la respuesta (mira las actualizaciones de la pregunta), te daré un par de días para editar tu respuesta y luego la aceptaré. –

0

Añadir @raises a su función de prueba. YO.e:

from nose.tools import raises 

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