2011-10-17 9 views
24

Estoy probando excepciones con la nariz. He aquí un ejemplo:¿Cómo deberíamos probar excepciones con la nariz?

def testDeleteUserUserNotFound(self): 
    "Test exception is raised when trying to delete non-existent users" 
    try: 
     self.client.deleteUser('10000001-0000-0000-1000-100000000000') 
     # make nose fail here 
    except UserNotFoundException: 
     assert True 

se ejecuta la aserción si se eleva la excepción, pero si se eleva no es una excepción, no se ejecutará.

¿Hay algo que pueda poner en la línea comentada arriba para que, si no se presenta ninguna excepción, la nariz informe una falla?

+0

casi un dup de http://stackoverflow.com/questions/11767938/how-to-use-noses-assert-raises – Stefano

Respuesta

39

nariz proporciona herramientas para probar las excepciones (como lo hace unittest). Prueba este ejemplo (y leído acerca de las otras herramientas en Nose Testing Tools

from nose.tools import * 

l = [] 
d = dict() 

@raises(Exception) 
def test_Exception1(): 
    '''this test should pass''' 
    l.pop() 

@raises(KeyError) 
def test_Exception2(): 
    '''this test should pass''' 
    d[1] 

@raises(KeyError) 
def test_Exception3(): 
    '''this test should fail (IndexError raised but KeyError was expected)''' 
    l.pop() 

def test_Exception4(): 
    '''this test should fail with KeyError''' 
    d[1] 

yo creo que este es el forma correcta que estabas buscando, ya que le permite ser específico acerca de las excepciones que usted espera o desea Así que en realidad provoca el error para ver que plantea la excepción correcta. Y luego deja que nose evalúe el resultado. (¡Ponga la menor lógica posible en las pruebas unitarias!)

+4

Gracias. Hay 'assert_raises' también por el aspecto de las cosas. – BartD

+2

nose también expone assert_raises que es solo self.assertRaises from unittest. Ese es conveniente para cuando necesita hacer más con la excepción en el formulario. con assertRaises (ValueError) como e:

1

No sé qué es la nariz, pero ha intentado usar 'else' después de la cláusula except. Es decir.

else: 
    assert False 
8
def testDeleteUserUserNotFound(self): 
    "Test exception is raised when trying to delete non-existent users" 
    try: 
     self.client.deleteUser('10000001-0000-0000-1000-100000000000') 
     assert False # <--- 
    except UserNotFoundException: 
     assert True 

La semántica de try/except implican que el flujo de ejecución sale del bloque de try en una excepción, por lo assert False no se ejecutará si se produce una excepción. Además, la ejecución no volverá a ingresar al bloque try una vez que el bloque except se haya ejecutado, por lo que no debería tener problemas.

 ↓ 
(statements) 
    ↓ exception 
    (try) ↚──────────→ (except) 
    ↓     │ 
(statements) ←───────────┘ 
    ↓ 
6

No sé por qué no está aquí todavía pero existe una forma más:

import unittest 

class TestCase(unittest.TestCase): 

    def testKeyError(self): 
     d = dict() 
     with self.assertRaises(KeyError): 
      d[1] 
+1

Este mecanismo no se menciona ya que la publicación trata específicamente de probar con nosetests, que no requiere el uso de clases o la herencia de 'unittests.TestCase' (la clase que define el' el método de assertRaises que mencionas). – PeterJCLaw

2

Uso assert_raises:

from nose.tools import assert_raises 

our_method   = self.client.deleteUser 
arg1    = '10000001-0000-0000-1000-100000000000' 
expected_exception = UserNotFoundException 

assert_raises(expected_exception, our_method, arg1) 

Usando tratar de atrapar en sus pruebas parece como una mala práctica (la mayoría de los casos).

No hay documentación específica en la nariz porque es básicamente una envoltura alrededor de unittest.TestCase.assertRaises (ref. How to use nose's assert_raises?)

4

recomiendo encarecidamente el uso de assert_raises y assert_raises_regexp de nose.tools, que duplican el comportamiento de assertRaises y assertRaisesRegexp de unittest.TestCase. Estos permiten el uso de la misma funcionalidad provista por unittest.TestCase en suites de prueba que en realidad no usan la clase unittest.TestCase.

Encuentro que @raises es un instrumento demasiado contundente. Aquí está el código que ilustra el problema:

from nose.tools import * 

something = ["aaa", "bbb"] 

def foo(x, source=None): 
    if source is None: 
     source = something 
    return source[x] 

# This is fine 
@raises(IndexError) 
def test1(): 
    foo(3) 

# This is fine. The expected error does not happen because we made 
# a mistake in the test or in the code. The failure indicates we made 
# a mistake. 
@raises(IndexError) 
def test2(): 
    foo(1) 

# This passes for the wrong reasons. 
@raises(IndexError) 
def test3(): 
    source = something[2] # This is the line that raises the exception. 
    foo(10, source) # This is not tested. 

# When we use assert_raises, we can isolate the line where we expect 
# the failure. This causes an error due to the exception raised in 
# the first line of the function. 
def test4(): 
    source = something[2] 
    with assert_raises(IndexError): 
     foo(10, source) 

test3 pases, pero no porque foo ha planteado la excepción que esperábamos pero debido a que el código que configura los datos a utilizar por foo falla con la misma excepción.test4 muestra cómo se puede escribir la prueba usando assert_raises para probar realmente lo que queremos probar. El problema en la primera línea provocará que Nose informe un error y luego podremos reescribir la prueba para que esa línea pueda finalmente probar lo que intentamos probar.

@raises no permite probar el mensaje asociado a la excepción. Cuando planteo ValueError, solo para tomar un ejemplo, generalmente quiero plantearlo con un mensaje informativo. He aquí un ejemplo:

def bar(arg): 
    if arg: # This is incorrect code. 
     raise ValueError("arg should be higher than 3") 

    if arg >= 10: 
     raise ValueError("arg should be less than 10") 

# We don't know which of the possible `raise` statements was reached. 
@raises(ValueError) 
def test5(): 
    bar(10) 

# Yes, we're getting an exception but with the wrong value: bug found! 
def test6(): 
    with assert_raises_regexp(ValueError, "arg should be less than 10"): 
     bar(10) 

test5 que utiliza @raises pasará, pero pasará por la razón equivocada. test6 realiza una prueba más fina que revela que el ValueError planteado no era el que queríamos.

Cuestiones relacionadas