2008-09-24 18 views

Respuesta

379

Uso TestCase.assertRaises (o TestCase.failUnlessRaises) del módulo unittest, por ejemplo:

import mymod 

class MyTestCase(unittest.TestCase): 
    def test1(self): 
     self.assertRaises(SomeCoolException, mymod.myfunc) 
+1

hay una manera de hacer lo contrario de esto? Al igual que falla solo si la función arroja la excepción? – BUInvent

+4

@BUInvent, sí, está: https://stackoverflow.com/a/4319870/3405140 – moertel

+0

Tenga en cuenta que para pasar argumentos a 'myfunc' debe agregarlos como argumentos a la llamada a assertRaises. Ver la respuesta de Daryl Spitzer. – cbron

6

Eche un vistazo al método assertRaises del módulo unittest.

24

el código debe seguir este patrón (esto es una prueba de estilo módulo unittest):

def test_afunction_throws_exception(self): 
    try: 
     afunction() 
    except ExpectedException: 
     pass 
    except Exception as e: 
     self.fail('Unexpected exception raised:', e) 
    else: 
     self.fail('ExpectedException not raised') 

En Python < 2.7 esta construcción es útil para verificar valores específicos en la excepción esperada. La función unittest assertRaises solo comprueba si se produjo una excepción.

+2

y el método self.fail toma solo un argumento – mdob

+3

Esto parece demasiado complicado para probar si una función arroja una excepción. Debido a que cualquier excepción que no sea la excepción arrojará un error al realizar la prueba y no lanzará una excepción fallará la prueba, parece que la única diferencia es que si obtiene una excepción diferente con 'assertRaises' obtendrá un ERROR en lugar de un FAIL. – unflores

160

El código en mi respuesta anterior se puede simplificar a:

def test_afunction_throws_exception(self): 
    self.assertRaises(ExpectedException, afunction) 

Y si ACONFIG toma argumentos, sólo tiene que pasar ellos en assertRaises así:

def test_afunction_throws_exception(self): 
    self.assertRaises(ExpectedException, afunction, arg1, arg2) 
+0

¿Cómo maneja la Trazabilidad de la excepción que está presente en la consola? Es decir, ahora hay un rastreo en la prueba de otro modo aprobada. – MadPhysicist

+0

@MadPhysicist No veo de qué estás hablando. Acabo de probar en Python 2.6 y 3.5, no se están imprimiendo Tracebacks en la consola. ¿Quizás está imprimiendo o registrando el rastreo en algún lugar del código? –

7

utilizo doctest [1 ] casi en todas partes porque me gusta el hecho de que documente y pruebe mis funciones al mismo tiempo.

Tener un vistazo a este código:

def throw_up(something, gowrong=False): 
    """ 
    >>> throw_up('Fish n Chips') 
    Traceback (most recent call last): 
    ... 
    Exception: Fish n Chips 

    >>> throw_up('Fish n Chips', gowrong=True) 
    'I feel fine!' 
    """ 
    if gowrong: 
     return "I feel fine!" 
    raise Exception(something) 

if __name__ == '__main__': 
    import doctest 
    doctest.testmod() 

Si se pone este ejemplo en un módulo y ejecuta desde la línea de comandos son evaluados y controlados ambos casos de prueba.

[1] Python documentation: 23.2 doctest -- Test interactive Python examples

+4

Me encanta el doctest, pero creo que complementa en lugar de reemplazar a unittest. – TimothyAWiseman

+2

¿Es menos probable que juegue bien con la refactorización automática? Supongo que una herramienta de refactorización diseñada para python * debería * estar al tanto de las cadenas de documentos. ¿Alguien puede comentar desde su experiencia? – kdbanman

4

acabo de descubrir que la Mock library proporciona un método assertRaisesWithMessage() (en su subclase unittest.TestCase), que comprobar no sólo que se eleva la excepción esperada, pero también que es levantado con el mensaje esperado:

from testcase import TestCase 

import mymod 

class MyTestCase(TestCase): 
    def test1(self): 
     self.assertRaisesWithMessage(SomeCoolException, 
            'expected message', 
            mymod.myfunc) 
+0

Desafortunadamente, ya no lo proporciona ... Pero la respuesta anterior de @Art (http://stackoverflow.com/a/3166985/1504046) da el mismo resultado – Rmatt

255

Como Python 2.7 se puede usar gestor de contexto para obtener una bodega del objeto real Excepción lanzada:

import unittest 

def broken_function(): 
    raise Exception('This is broken') 

class MyTestCase(unittest.TestCase): 
    def test(self): 
     with self.assertRaises(Exception) as context: 
      broken_function() 

     self.assertTrue('This is broken' in context.exception) 

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

http://docs.python.org/dev/library/unittest.html#unittest.TestCase.assertRaises


En Python 3.5, hay que envolver context.exception en str, de lo contrario obtendrá un TypeError

self.assertTrue('This is broken' in str(context.exception)) 
+3

Estoy usando Python 2.7.10, y el arriba no funciona; 'context.exception' no da el mensaje; es un tipo. – LateCoder

+3

También en Python 2.7 (al menos en mi versión 2.7.6) usando 'import unittest2', necesitas usar' str() ', es decir' self.assertTrue ('Esto está roto' en str (context.exception)) ' . –

+1

Dos cosas: 1. Puede usar assertIn en lugar de assertTrue. P.ej. self.assertIn ('Esto está roto', context.exception) 2. En mi caso, usando 2.7.10, context.exception parece ser una matriz de caracteres. Usar str no funciona. Terminé haciendo esto: '' .join (context.exception) Por lo tanto, juntar: self.assertIn ('Esto está roto', '' .join (context.exception)) – blockcipher

0

Usted puede utilizar assertRaises del módulo unittest

import unittest 

class TestClass(): 
    def raises_exception(self): 
    raise Exception("test") 

class MyTestCase(unittest.TestCase): 
    def test_if_method_raises_correct_exception(self): 
    test_class = TestClass() 
    # note that you dont use() when passing the method to assertRaises 
    self.assertRaises(Exception, test_class.raises_exception) 
7

de: http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/

Primero, aquí está la función correspondiente (todavía dum: p) en el archivo dum_function.PY:

def square_value(a): 
    """ 
    Returns the square value of a. 
    """ 
    try: 
     out = a*a 
    except TypeError: 
     raise TypeError("Input should be a string:") 

    return out 

Aquí está la prueba a realizar (sólo se inserta esta prueba):

import dum_function as df # import function module 
import unittest 
class Test(unittest.TestCase): 
    """ 
     The class inherits from unittest 
     """ 
    def setUp(self): 
     """ 
     This method is called before each test 
     """ 
     self.false_int = "A" 

    def tearDown(self): 
     """ 
     This method is called after each test 
     """ 
     pass 
     #--- 
     ## TESTS 
    def test_square_value(self): 
     # assertRaises(excClass, callableObj) prototype 
     self.assertRaises(TypeError, df.square_value(self.false_int)) 

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

Ahora estamos listos para probar nuestra función! Esto es lo que sucede cuando se trata de ejecutar la prueba:

====================================================================== 
ERROR: test_square_value (__main__.Test) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "test_dum_function.py", line 22, in test_square_value 
    self.assertRaises(TypeError, df.square_value(self.false_int)) 
    File "/home/jlengrand/Desktop/function.py", line 8, in square_value 
    raise TypeError("Input should be a string:") 
TypeError: Input should be a string: 

---------------------------------------------------------------------- 
Ran 1 test in 0.000s 

FAILED (errors=1) 

El TypeError es actullay levantado, y genera un fallo de la prueba. El problema es que este es exactamente el comportamiento que queríamos: s.

Para evitar este error, sólo tiene que ejecutar la función usando lambda en la llamada de prueba:

self.assertRaises(TypeError, lambda: df.square_value(self.false_int)) 

La salida final:

---------------------------------------------------------------------- 
Ran 1 test in 0.000s 

OK 

perfecto!

... ¡y para mí también es perfecto!

Thansk mucho el Sr. Julien Lengrand-Lambert

+8

Solo una nota, no necesitas la lambda. La línea 'self.assertRaises (TypeError, df.square_value (self.false_int))' llama al método y devuelve el resultado. Lo que quiere es pasar el método y cualquier argumento y dejar que la prueba unit lo llame: 'self.assertRaises (TypeError, df.square_value, self.false_int)' –

2

Usted puede construir su propia contextmanager para comprobar si se produce la excepción.

import contextlib 

@contextlib.contextmanager 
def raises(exception): 
    try: 
     yield 
    except exception as e: 
     assert True 
    else: 
     assert False 

Y entonces usted puede utilizar raises así:

with raises(Exception): 
    print "Hola" # Calls assert False 

with raises(Exception): 
    raise Exception # Calls assert True 

Si está utilizando pytest, esto se implementa ya. Usted puede hacer pytest.raises(Exception):

Ejemplo:

def test_div_zero(): 
    with pytest.raises(ZeroDivisionError): 
     1/0 

Y el resultado:

[email protected]$ py.test 
================= test session starts ================= 
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python 
collected 1 items 

tests/test_div_zero.py:6: test_div_zero PASSED 
73

¿Cómo se prueba que una función de Python lanza una excepción?

¿Cómo se escribe una prueba que falla solo si una función no arroja una excepción esperada?

respuesta corta:

utilizar el método de self.assertRaises como un gestor de contexto:

def test_1_cannot_add_int_and_str(self): 
     with self.assertRaises(TypeError): 
      1 + '1' 

Demostración

El enfoque de mejores prácticas es bastante fácil de demostrar en un terminal de Python.

El unittest biblioteca

En Python 2.7 o 3:

import unittest 

En Python 2.6, se puede instalar un backport de unittest la biblioteca de 2,7, llamados unittest2, y justo como alias que unittest:

import unittest2 as unittest 

ejemplo se comprueba

Ahora, pegue en su shell de Python la siguiente prueba de seguridad de tipo de Python:

class MyTestCase(unittest.TestCase): 
    def test_1_cannot_add_int_and_str(self): 
     with self.assertRaises(TypeError): 
      1 + '1' 
    def test_2_cannot_add_int_and_str(self): 
     import operator 
     self.assertRaises(TypeError, operator.add, 1, '1') 

La prueba uno usa assertRaises como gestor de contexto, lo que garantiza que el error se capta y se limpia correctamente, mientras se graba.

También podríamos escribirlo sin gestor de contexto, ver prueba dos. El primer argumento sería el tipo de error que espera plantear, el segundo argumento, la función que está probando y los argumentos restantes y los argumentos de palabras clave se pasarán a esa función.

Creo que es mucho más simple, legible y fácil de mantener el uso del gestor de contexto.

Ejecución de las pruebas

Para ejecutar las pruebas:

unittest.main(exit=False) 

En Python 2.6, es probable que need the following:

unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase)) 

Y su terminal debe emitir el siguiente:

.. 
---------------------------------------------------------------------- 
Ran 2 tests in 0.007s 

OK 
<unittest2.runner.TextTestResult run=2 errors=0 failures=0> 

Y vemos que, como esperamos, intentar agregar un 1 y un resultado '1' en un TypeError.


Para una salida más prolija, intente esto:

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase)) 
Cuestiones relacionadas