2009-06-15 22 views
10

Solo soy un principiante en Python y la programación en general, y tengo algunas preguntas sobre el módulo unittest.Pruebas unitarias para excepciones en el constructor Python

Tengo una clase, y en el método __init__ estoy haciendo algunas afirmaciones para comprobar si hay malos argumentos. Me gustaría crear una prueba unitaria que compruebe tal AsertiónError al crear nuevas instancias.

En el módulo unittest, se puede realizar una prueba (con assertRaises) para excepciones específicas cuando se llama a un llamante, pero obviamente eso se aplicará a los métodos de la clase. ¿Cuál es la forma correcta de ejecutar dicha prueba para el constructor?

Sé que puedo intentar crear una instancia de la clase con argumentos incorrectos, y unittest informará una falla de prueba, pero que se detiene inmediatamente después de la primera excepción, e incluso si puedo envolver varias pruebas en múltiples funciones de prueba, simplemente no parece elegante en absoluto.

Respuesta

1

Bueno, como punto de partida, comprobar si hay malos argumentos no es una buena idea en python. Python es dinámicamente fuerte tipeado por una razón.

Simplemente debe asumir que los argumentos son buenos argumentos. Nunca se sabe cuál es la intención de los usuarios de su clase, por lo que al verificar si hay buenos argumentos es una manera de limitar el uso de su clase en instancias más genéricas.

En su lugar define una buena API y la documenta muy bien, usando docstrings y texto, y deja que los errores de los argumentos erróneos fluyan automáticamente al usuario.

Ejemplo:

def sum_two_values(value_a, value_b): 
    return value_a + value_b 

bien, este ejemplo es estúpida, pero si puedo comprobar y afirmar el valor como número entero, la función no funcionará con flotadores, cuerdas, lista, por ninguna otra razón que mi verificar, entonces, ¿por qué verificar en primer lugar? Fallará automáticamente con tipos que no funcionarían, por lo que no tiene que preocuparse.

+0

Bueno, en mi caso particular, me gustaría afirmar que algo ha ido mal desde el principio, ya que más tarde cargué algunas bibliotecas C con ctypes y tengo que pasarles los argumentos del constructor ... –

+0

@asen_asenov: En ese caso , solo envuelva su llamada ctypes con la transformación adecuada, por ejemplo ctypes.c_int (param). Eso asegurará que el tipo sea compatible y generará un error para usted en tipos incompatibles automáticamente; – nosklo

7

No te metas con assertRaises. Es demasiado complicado.

Haga esto

class Test_Init(unittest.TestCase): 
    def test_something(self): 
     try: 
      x= Something("This Should Fail") 
      self.fail("Didn't raise AssertionError") 
     except AssertionError, e: 
      self.assertEquals("Expected Message", e.message) 
      self.assertEquals(args, e.args) 

Cualquier otra excepción será un error en la prueba ordinaria.

Además, no se meta con demasiada verificación de errores por adelantado en un método __init__. Si alguien proporciona un objeto del tipo incorrecto, su código fallará en el curso normal de los eventos y generará una excepción normal por medios normales. No necesita "pre-seleccionar" los objetos demasiado.

+2

¿Puedes ampliar "Es demasiado complicado"? He estado teniendo problemas con 'assertRaises (assertionError, ...)' (la afirmación se plantea, pero no ha sido capturada por 'assertRaises'), y sería interesante saber por qué ... – naught101

+0

Más molesto, este código no Realmente funciona con AssertionErrors, porque no tienen una propiedad 'message' (al menos en python 3). – naught101

17

En el módulo unittest, se puede probar (con assertRaises) para la excepción específica cuando un exigible se llama, pero es evidente que se aplicará a los métodos de la clase. ¿Cuál es la forma correcta de ejecutar dicha prueba para el constructor?

El constructor es en sí un callable:

self.assertRaises(AssertionError, MyClass, arg1, arg2) 

Dicho esto, quiero hacer eco de las preocupaciones de nosklo y S. Lott acerca de hacer la comprobación de tipos de argumentos. Además, no debería usar aserciones para verificar los argumentos de las funciones: las aserciones son más útiles como verificaciones de cordura que no se activarán a menos que algo esté internamente mal con su código.Además, las declaraciones de afirmación se compilan cuando Python se ejecuta en el modo "optimizado" -O. Si una función necesita hacer algún tipo de comprobación de sus argumentos, debería plantear una excepción adecuada.

+1

-1: Las afirmaciones son una excelente manera de verificar argumentos. Código muy corto: afirme 0 <= n, "n es negativo". –

+0

Supongo que es una cuestión de opinión, entonces. – Miles

0

respuesta de S. Lott es incorrecta: self.fail() misma lanza una excepción, que luego será causado por la excepción en la siguiente línea:

class NetworkConfigTest1(unittest.TestCase): 
    def runTest(self): 
     try: 
      NetworkConfig("192.168.256.0/24") 
      self.fail("Exception expected but not thrown") 
     except Exception, error: 
      printf("Exception caught: %s" % str(error) 
      pass 

La salida fue "excepción esperada pero no tirado", pero el la prueba unitaria no se marcó como una falla, ¡aunque el código que se estaba probando no se había escrito!

Una forma más válida para comprobar si un método lanza una excepción sería utilizar:

self.failUnlessRaises([error], [callable], [arguments to callable]) 

En mi caso, la clase que soy prueba se llama NetworkConfig, y el constructor debe lanzar una excepción si el descriptor de red no es válido. Y lo que funcionó es:

class NetworkConfigTest1(unittest.TestCase): 
    def runTest(self): 
     self.failUnlessRaises(Exception, NetworkConfig, "192.168.256.0/24") 

Esto funciona como se desee y realiza la prueba correcta.

2

no sabe es que esta ayuda, sino un problema que tenía es la siguiente:

self.assertRaises(Exception, MyFunction()) 

problema es que no estaba de paso MyFunction, pero llamándolo así, lo que resulta en un fracaso y Excepción ser criado. Myfunction espera un argumento, y quiero que fracase si no se pasa en mí frustrado por un tiempo hasta que lo he descubierto:.

self.assertRaises(Exception, MyFunction) 

funciona como se espera.