2009-05-30 14 views
8

Es común tener clases con métodos con parámetros de cadena que debe ser validado agains nulo o vacío, como este ejemplo:¿Cómo debo probar los parámetros de cadena vacía y/o vacía en un método?

public class MyClass { 

    public void MyMethod(string param){ 

     if(string.IsNullOrEmpty(param)){ 
      throw new ArgumentNullException(...); 
     } 
     //... 
    } 
} 

Está claro que el comportamiento del método es el mismo para ambos (no válido) valores. Esta es una situación muy común, y cuando se trata de probar estos métodos, siempre dudo sobre cómo hacerlo. Siempre creo dos pruebas separadas para estos casos:

[TestClass] 
public class Tests { 

    [TestMethod] 
    public void MyMethod_should_fail_if_param_is_null(){ 
     //... 
     myclass.MyMethod(null); 
     //... 
    } 

    [TestMethod] 
    public void MyMethod_should_fail_if_param_is_empty(){ 
     //... 
     myclass.MyMethod(""); 
     //... 
    } 

}

Pero veo demasiada redundancia. Esas pruebas son exactamente iguales, con la única diferencia de que el parámetro pasó al método. Eso me molesta mucho, ya que tengo que crear dos pruebas para cada parámetro de cadena. Un método con 3 parámetros tendría 6 pruebas solo para probar los parámetros.

Creo que esta es la manera correcta de probar esos parámetros, pero si sé que el 99% de los parámetros de cadena se validarán de la misma manera, ¿no sería mejor probarlos para null (o vacíos) y suponer que el comportamiento en el otro caso será el mismo?

Me gustaría saber qué opina sobre esto. Sé que lo que estoy pidiendo es más una opinión técnica que una técnica, pero creo que la comunidad de pruebas puede tener algo interesante que decir sobre esta situación.

¡Gracias!

Respuesta

11

Personalmente yo considere usando una sola prueba para todos los parámetros. Eso no sigue el dogma normal de las pruebas unitarias, pero aumenta la legibilidad de las pruebas (al minimizar la cantidad de código de prueba que se dedica a un caso bastante repetitivo) y no tiene mucho en el camino de las desventajas. Sí, si la prueba falla, no se sabe si también fallarán todas las comprobaciones posteriores a la primera falla, ¿pero realmente es realmente un problema en la práctica?

Lo importante es asegurarse de que tiene un atajo para probar la carcasa. Por ejemplo, podría escribir algo como esto (si su marco de pruebas unitarias no lo tiene ya):

public static void ExpectException<T>(Action action) where T : Exception 
{ 
    try 
    { 
     action(); 
     Assert.Fail("Expected exception " + typeof(T).Name); 
    } 
    catch (T exception) 
    { 
     // Expected 
    } 
} 

entonces usted puede escribir:

[Test] 
public void MyMethodFailsWithInvalidArguments() 
{ 
    ExpectException<ArgumentNullException>(() => myClass.MyMethod(null)); 
    ExpectException<ArgumentException>(() => myClass.MyMethod("")); 
} 

Mucho más concisa que hacer cada uno con un bloque try/catch individual, o incluso utilizando un atributo ExpectedException y múltiples pruebas.

Es posible que desee sobrecargas para casos en los que también desee verificar que en cada caso no se hayan tocado objetos falsificados (para comprobar que se evitan los efectos secundarios) o posibles sobrecargas para excepciones comunes como ArgumentNullException.

Para los métodos de un solo parámetro incluso se podría escribir un método para encapsular exactamente lo que necesita:

public void ExpectExceptionForNullAndEmptyStrings(Action<string> action) 
{ 
    ExpectException<ArgumentNullException>(() => action(null)); 
    ExpectException<ArgumentException>(() => action("")); 
} 

luego llamar con:

[Test] 
public void MyMethodFailsWithInvalidArguments() 
{ 
    // This *might* work without the 
    ExpectExceptionForNullAndEmptyStrings(myClass.MyMethod); 
} 

... y tal vez otro para métodos con un solo parámetro pero un tipo de devolución no nulo.

Eso es, posiblemente, va un poco lejos aunque :)

+0

Gracias por su respuesta rápida! Ya creé mi propia versión de ExpectException (puedes verla aquí http://gerardocontijoch.wordpress.com/2008/12/02/uso-de-expectedexceptionattribute-para-testear-excepciones/), y úsala en estos casos. No puedo vivir sin él: P Me gusta la solución que propones, pensé en ello, pero como dijiste, "eso no sigue el dogma normal de las pruebas unitarias" y solo quería seguirlo, ya sabes , acostúmbrate a las buenas prácticas. Probablemente vaya por esta solución después de todo. Veamos lo que otros piensan ... –

+1

La cosa es que no creo que seguir el dogma y arrojar el pragmatismo por la ventana * sea * una buena práctica :) Vale la pena * conocer * el dogma, pero luego ignorarlo conscientemente cuando He sopesado los pros y los contras de una situación particular y encontré que no funcionaba. –

+0

Estoy totalmente de acuerdo con usted y ese fue el motivo de mi pregunta. Encontré los inconvenientes de seguir una cierta "buena" práctica, pero no muchos profesionales (una especie de contradicción, una buena práctica con más inconvenientes que profesionales), así que publiqué una pregunta para ver si había algo que me faltaba. –

-2

Si utiliza Java y JUnit puede utilizar esta sintaxis

@Test(expected = IllegalArgumentException.class) 
public void ArgumentTest() { 
    myClass.MyMethod(""); 
    myClass.MyMethod(null); 
} 
+4

Ese método pasará si * al menos una * de esas llamadas al método arroja la excepción correcta, en lugar de si * both * do. Es un error fácil de realizar, desafortunadamente, ese tipo de prueba solo es útil (IMO) cuando se trata de una declaración única. De lo contrario, no estás probando exactamente dónde se lanza la excepción. –

+0

Sí, tienes razón ... deberían ser dos pruebas ... – Janusz

Cuestiones relacionadas