2011-06-15 18 views
5

Estoy reflexionando sobre el manejo de excepciones y las mejores prácticas de pruebas unitarias porque estamos tratando de implementar algunas de las mejores prácticas de código.JUnit4 @Test (expected = MyException.class) VS try/catch

Un artículo anterior sobre mejores prácticas, que se encuentra en la wiki de nuestra compañía, indicó "No use try/catch, pero use Junit4 @Test (expect = MyException.class)", sin más información. No estoy convencido.

Muchas de nuestras excepciones personalizadas tienen un Enum para identificar la causa del error. Como resultado de ello, prefiero ver una prueba como:

@Test 
public void testDoSomethingFailsBecauseZzz() { 
try{ 
    doSomething(); 
} catch(OurCustomException e){ 
    assertEquals("Omg it failed, but not like we planned", FailureEnum.ZZZ, e.getFailure()); 
} 
} 

que:

@Test(expected = OurCustomException.class) 
public void testDoSomethingFailsBecauseZzz() { 
    doSomething(); 
} 

cuando doSomethig() se parece a:

public void doSomething throws OurCustomException { 
    if(Aaa) { 
    throw OurCustomException(FailureEnum.AAA); 
    } 
    if(Zzz) { 
    throw OurCustomException(FailureEnum.ZZZ); 
    } 
    // ... 
} 

En una nota lateral, yo soy más que convencidos de que en algunos casos @Test (expected = blabla.class) ES la mejor opción (por ejemplo, cuando la excepción es precisa y no puede haber ninguna duda sobre qué la causa).

¿Me falta algo aquí o debo presionar el uso de try/catch cuando sea necesario?

Respuesta

5
  • Si simplemente desea comprobar que se produjo una excepción de un cierto tipo, utilice la propiedad de la anotación expected.
  • Si desea comprobar las propiedades de la excepción lanzada (por ejemplo, el mensaje o un valor de miembro personalizado), póngala en la prueba y realice afirmaciones.

En su caso, parece que desea esto último (para afirmar que la excepción tiene un cierto valor FailureEnum); no hay nada de malo con el uso del try/catch.

La generalización de que no debe "usar try/catch" (interpretado como "nunca") es litera.

Jeff tiene razón; la organización de su jerarquía de excepciones es sospechosa. Sin embargo, pareces reconocer esto. :)

+0

De hecho, estoy bastante feliz cuando los veo diciendo que la jerarquía de excepciones es sospechosa, me ha estado molestando por un tiempo. (Puede ser por eso que estoy reflexionando sobre estos temas;)). ¡Gracias! – Stph

6

Parece que su enumeración se está utilizando como alternativa a una jerarquía de excepciones? Quizás si tuviera una jerarquía de excepciones, ¿sería más útil el @Test(expected=XYZ.class)?

+1

Sí, realmente les gustan los atributos Enum y String en lugar de un código real orientado a objetos;) Esa puede ser una de las raíces de todos los males en algunos de nuestros códigos de manejo de excepciones ;) – Stph

+2

Si está reflexionando sobre las mejores prácticas, ¿por qué no considera volver a factorizar a una jerarquía de excepciones? ¿Lo hace más útil y sostenible en el futuro? –

+0

Hmm, la refactorización de toda la aplicación lamentablemente no es posible en este momento. Eventualmente va a ser necesario, pero no tengo ese tipo de poder o tiempo;) Sin embargo, si alguna vez tengo que comenzar un proyecto desde cero o ayudar con la refactorización, ciertamente impulsaré el uso de una jerarquía de excepciones correcta :) – Stph

1

En el caso especial, que desea probar (1) si el esperado excepción tipo es lanzada y (2) si el número de error es correcto, ya que el método puede tirado la misma excepción con diferentes tipos .

Esto requiere una inspección del objeto de excepción. Sin embargo, usted puede pegarse a la recomendación y verificar que la excepción derecho ha sido lanzado:

@Test(expected = OurCustomException.class) 
public void testDoSomethingFailsBecauseZzz() { 
    try { 
     doSomething(); 
    } catch (OurCustomException e) { 
     if (e.getFailureEnum.equals(FailureEnum.ZZZ)) // use *your* method here 
     throw e; 

     fail("Catched OurCostomException with unexpected failure number: " 
     + e.getFailureEnum().getValue()); // again: your enum method here 
    } 
} 

Este patrón se comer la excepción inesperado y hacer que la prueba falle.

Editar

lo cambió porque me perdí lo obvio: podemos hacer un caso de prueba fallar y capturar un mensaje. Así que ahora: la prueba pasa, si la excepción esperada con se arroja el código de error esperado. Si la prueba falla porque recibimos un error inesperado, entonces podemos leer el código de error.

+0

¿No queremos evitar comer excepciones inesperadas? Además, realmente no me quiero apegar a la recomendación (pero si tengo que hacerlo, me gusta su solución);) – Stph

+0

@Stph - gracias por su comentario, he mejorado mi respuesta. Simplemente se olvidó, que podemos hacer que falle con un mensaje de error;) –

2

Si desea comprobar el tipo de excepción en bruto, entonces el método expected es apropiado. De lo contrario, si necesita probar algo acerca de la excepción (e independientemente de las pruebas de rareza de enum, el contenido del mensaje es común) puede intentar capturar, pero eso es un poco antiguo. La nueva forma de JUnit para hacerlo es con un MethodRule. El que viene en la API (ExpectedException) se trata de probar el mensaje específicamente, pero puede ver fácilmente el código y adaptar esa implementación para verificar si hay fallas enum s.

+0

Hmm. MethodRule, nunca he escuchado sobre. Eso es algo que me faltaba;) ¡Gracias! – Stph

0

Hice catch-exception porque estaba enfrentando el mismo problema que usted, Stph. Con excepción de captura el código podría tener este aspecto:

@Test 
public void testDoSomethingFailsBecauseZzz() { 
    verifyException(myObj, OurCustomException.class).doSomething(); 
    assertEquals("Omg it failed, but not like we planned", FailureEnum.ZZZ,  
       ((OurCustomException)caughtException()).getFailure() ; 
} 
1

me encontré con este cuando se busca la manera de manejar las excepciones.

Como se menciona en @Yishai, la forma preferida de esperar excepciones es utilizando las reglas JUnit y ExpectedException.

Al usar @Test(expected=SomeException.class), pasará un método de prueba si se lanza la excepción a alguna parte del método.

Cuando se utiliza ExpectedException:

@Test 
public void testException() 
{ 
    // If SomeException is thrown here, the test will fail. 
    expectedException.expect(SomeException.class); 
    // If SomeException is thrown here, the test will pass. 
} 

También puede ensayo:

  • un mensaje esperado: ExpectedException.expectMessage();
  • una causa esperada: expectedException.expectCause().

Como nota al margen: no creo que el uso de enumeraciones de mensajes/causas de excepción sea una buena práctica. (Por favor, corríjanme si me equivoco.)