2011-12-29 14 views
46

Para asegurarme de que los mensajes de error de mi módulo son informativos, me gustaría ver todos los mensajes de error capturados por assertRaises(). Hoy lo hago para cada assertRaises(), pero como hay muchos en el código de prueba, se vuelve muy tedioso.¿Cómo se muestran los mensajes de error capturados por assertRaises() en unittest en Python2.7?

¿Cómo puedo imprimir los mensajes de error para todas las assertRaises()? Estudié la documentación en http://docs.python.org/library/unittest.html sin encontrar la manera de resolverla. ¿Puedo de alguna manera monopatch el método assertRaises()? Prefiero no cambiar todas las líneas de assertRaises() en el código de prueba, ya que con mayor frecuencia uso el código de prueba de la manera estándar.

supongo que esta pregunta está relacionada con Python unittest: how do I test the argument in an Exceptions?

Así es como lo hago hoy. Por ejemplo:

#!/usr/bin/env python 

def fail(): 
    raise ValueError('Misspellled errrorr messageee') 

Y el código de prueba:

#!/usr/bin/env python 
import unittest 
import failure 

class TestFailureModule(unittest.TestCase): 

    def testFail(self): 
     self.assertRaises(ValueError, failure.fail) 

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

Para comprobar el mensaje de error, simplemente cambiar el tipo de error en los assertRaises() para, por ejemplo, IOError. Entonces puedo ver el mensaje de error:

E 
====================================================================== 
ERROR: testFail (__main__.TestFailureModule) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
File "test_failure.py", line 8, in testFail 
    self.assertRaises(IOError, failure.fail) 
    File "/usr/lib/python2.7/unittest/case.py", line 471, in assertRaises 
    callableObj(*args, **kwargs) 
File "/home/jonas/Skrivbord/failure.py", line 4, in fail 
    raise ValueError('Misspellled errrorr messageee') 
ValueError: Misspellled errrorr messageee 

---------------------------------------------------------------------- 
Ran 1 test in 0.001s 

FAILED (errors=1) 

¿Alguna sugerencia? /Jonas

EDIT:

Con los consejos de Robert Rossney me las arreglé para resolver el problema. No está destinado principalmente a errores ortográficos, sino a garantizar que los mensajes de error sean realmente significativos para el usuario del módulo. La funcionalidad normal de unittest (así es como la uso la mayor parte del tiempo) se logra configurando SHOW_ERROR_MESSAGES = False.

Simplemente reemplazo el método assertRaises(), como se ve a continuación. ¡Funciona como el encanto!

SHOW_ERROR_MESSAGES = True 

class NonexistantError(Exception): 
    pass 

class ExtendedTestCase(unittest.TestCase): 
    def assertRaises(self, excClass, callableObj, *args, **kwargs): 
     if SHOW_ERROR_MESSAGES: 
      excClass = NonexistantError 
     try: 
      unittest.TestCase.assertRaises(self, excClass, callableObj, *args, **kwargs) 
     except: 
      print '\n ' + repr(sys.exc_info()[1]) 

Una fracción de la salida resultante:

testNotIntegerInput (__main__.TestCheckRegisteraddress) ... 
    TypeError('The registeraddress must be an integer. Given: 1.0',) 

    TypeError("The registeraddress must be an integer. Given: '1'",) 

    TypeError('The registeraddress must be an integer. Given: [1]',) 

    TypeError('The registeraddress must be an integer. Given: None',) 
ok 
testCorrectNumberOfBytes (__main__.TestCheckResponseNumberOfBytes) ... ok 
testInconsistentLimits (__main__.TestCheckNumerical) ... 
    ValueError('The maxvalue must not be smaller than minvalue. Given: 45 and 47, respectively.',) 

    ValueError('The maxvalue must not be smaller than minvalue. Given: 45.0 and 47.0, respectively.',) 
ok 
testWrongValues (__main__.TestCheckRegisteraddress) ... 
    ValueError('The registeraddress is too small: -1, but minimum value is 0.',) 

    ValueError('The registeraddress is too large: 65536, but maximum value is 65535.',) 
ok 
testTooShortString (__main__.TestCheckResponseWriteData) ... 
    ValueError("The payload is too short: 2, but minimum value is 4. Given: '\\x00X'",) 

    ValueError("The payload is too short: 0, but minimum value is 4. Given: ''",) 

    ValueError("The writedata is too short: 1, but minimum value is 2. Given: 'X'",) 

    ValueError("The writedata is too short: 0, but minimum value is 2. Given: ''",) 
ok 
testKnownValues (__main__.TestCreateBitPattern) ... ok 
testNotIntegerInput (__main__.TestCheckSlaveaddress) ... 
    TypeError('The slaveaddress must be an integer. Given: 1.0',) 

    TypeError("The slaveaddress must be an integer. Given: '1'",) 

    TypeError('The slaveaddress must be an integer. Given: [1]',) 

    TypeError('The slaveaddress must be an integer. Given: None',) 
ok 
+4

Por qué seguir utilizando assertRaises si es necesario comprobar los argumentos? ¿Por qué no simplemente atrapar la excepción y examinarla usando 'try' y' except'? –

Respuesta

34

Out-of-the-box unittest no hace esto. Si esto es algo que quieres hacer con frecuencia, puede intentar algo como esto:

class ExtendedTestCase(unittest.TestCase): 

    def assertRaisesWithMessage(self, msg, func, *args, **kwargs): 
    try: 
     func(*args, **kwargs) 
     self.assertFail() 
    except Exception as inst: 
     self.assertEqual(inst.message, msg) 

Deducir sus clases de prueba de unidad de ExtendedTestCase en lugar de unittest.TestCase.

Pero realmente, si simplemente le preocupan los mensajes de error mal escritos, y le preocupa lo suficiente como para querer crear casos de prueba al respecto, no debería incluir mensajes como literales de cadenas. Debe hacer con ellos lo que hace con otras cadenas importantes: definirlas como constantes en un módulo que importa y que alguien es responsable de la corrección de pruebas. Un desarrollador que escribe mal las palabras en su código también las escribirá mal en sus casos de prueba.

+11

+1 por "Un desarrollador que escribe mal las palabras en su código también las escribirá mal en sus casos de prueba". – Johnsyweb

+7

Para mí, es mucho más atroz cuando pruebas ver que se está produciendo un error específico, pero una prueba puede 'pasar' debido a efectos secundarios no deseados. P.ej. el error que esperaba no se planteó, pero el mismo tipo de error se plantea en otro lugar, por lo tanto, satisface la prueba. La prueba pasa, el código está bloqueado. El mismo trato para los errores que subclasifican el error que está buscando: si su prueba es demasiado general, termina atrapando algo que no espera. –

+1

Debe utilizar '' inst.args [0] '' en lugar de '' inst.message'' Para ejecutar este código tanto en Python 2 y Python 3 – oblalex

71

Una vez preferí la respuesta más excelente dada por @Robert Rossney.Hoy en día, yo prefiero usar assertRaises como un gestor de contexto (una nueva capacidad en unittest2) de esta manera:

with self.assertRaises(TypeError) as cm: 
    failure.fail() 
self.assertEqual(
    'The registeraddress must be an integer. Given: 1.0', 
    str(cm.exception) 
) 
+0

NB. assertRaises se puede usar como administrador de contexto desde 'unittest' en Python 2.7. características de backports de unittest2 para versiones anteriores de Python. http://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises – powlo

+0

¿Qué pasa si el código falla en la parte "con" .... en mi caso, que con una parte falla ... .so quiero mostrar un mensaje ... como podemos hacer para otras afirmaciones simples, por ejemplo, self.assertEqual (cm.exception.faultCode, 101001, 'El código de error no coincide con el código de error esperado% d'% 101001) –

+0

@arindamroychowdhury, Lo siento, pero no he codificado Python en bastante tiempo, así que no sé la respuesta a tu pregunta. La mejor de las suertes. Tal vez, una de las otras personas aquí podría responder mejor a tu pregunta. Buena suerte. – mkelley33

34

Usted está buscando assertRaisesRegexp, que está disponible desde Python 2.7. A partir de los documentos:

self.assertRaisesRegexp(ValueError, 'invalid literal for.*XYZ$', int, 'XYZ') 

o:

with self.assertRaisesRegexp(ValueError, 'literal'): 
    int('XYZ') 
+0

sí, pero si el error esperado no se aumenta, nunca verá el mensaje/no puede cambiar el predeterminado. Extremadamente incómodo cuando se prueban algunos parámetros en bucle: no se sabe para qué parámetro pasa la función sin el error esperado. – Mesco

4

mkelley33 da respuesta agradable, pero este enfoque puede ser detectada como emisión por algunas herramientas de análisis de código como Codacy. El problema es que no sabe que assertRaises se puede utilizar como gestor de contexto e informa que no todos los argumentos se pasan al assertRaisesmétodo.

lo tanto, me gustaría mejorar la respuesta de Robert Rossney:

class TestCaseMixin(object): 

    def assertRaisesWithMessage(self, exception_type, message, func, *args, **kwargs): 
     try: 
      func(*args, **kwargs) 
     except exception_type as e: 
      self.assertEqual(e.args[0], message) 
     else: 
      self.fail('"{0}" was expected to throw "{1}" exception' 
         .format(func.__name__, exception_type.__name__)) 

diferencias clave son:

  1. Probamos tipo de excepción.
  2. Podemos ejecutar este código tanto en Python 2 como en Python 3 (llamamos a e.args[0] porque los errores en Py3 no tienen el atributo message).
8

Si desea que el mensaje de error que coincida exactamente algo:

with self.assertRaises(ValueError) as error: 
    do_something() 
self.assertEqual(error.exception.message, 'error message') 
Cuestiones relacionadas