¿Cómo mostrar los mensajes de error capturados por assertRaises () en unittest en Python2.7?

Para asegurarme de que los mensajes de error de mi módulo sean informativos, me gustaría ver todos los mensajes de error detectados por assertRaises (). Hoy lo hago para cada assertRaises (), pero como hay muchos de ellos en el código de prueba, se vuelve muy tedioso.

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

Supongo que esta pregunta está relacionada con la prueba de unidad de Python: ¿cómo puedo probar el argumento en una excepción?

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 verificar el mensaje de error, simplemente cambio el tipo de error en assertRaises () a, 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

EDITAR:

Con las sugerencias de Robert Rossney logré resolver el problema. No se destina principalmente a errores de ortografía, sino a asegurarse de 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 anulo 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 

unittest de unittest fuera de la caja no hacen esto. Si esto es algo que quieres hacer con frecuencia, puedes 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) 

Derive las clases de prueba de unidad de ExtendedTestCase lugar de unittest.TestCase .

Pero en realidad, si simplemente está preocupado por los mensajes de error mal escritos y lo suficientemente preocupado como para querer crear casos de prueba a su alrededor, no debe incluir mensajes como literales de cadena. Debe hacer con ellos lo que haga con otras cadenas importantes: definirlas como constantes en un módulo que importe y que alguien es responsable de la revisión. Un desarrollador que escribe incorrectamente las palabras en su código también las escribe incorrectamente en sus casos de prueba.

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

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

Está buscando assertRaisesRegexp , que está disponible desde Python 2.7. De los documentos:

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

o:

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

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

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

mkelley33 da una buena respuesta, pero este enfoque puede ser detectado como un problema por algunas herramientas de análisis de código como Codacy . El problema es que no sabe que assertRaises se puede usar como administrador de contexto e informa que no todos los argumentos se pasan al método assertRaises .

Entonces, 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__)) 

Las diferencias clave son:

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