2010-06-11 18 views
28

Usamos JUnit 3 en el trabajo y no hay ninguna anotación ExpectedException. Quería añadir una utilidad en nuestro código para terminar con esto:¿Captura una excepción genérica en Java?

try { 
    someCode(); 
    fail("some error message"); 
} catch (SomeSpecificExceptionType ex) { 
} 

así que he intentado esto:

public static class ExpectedExceptionUtility { 
    public static <T extends Exception> void checkForExpectedException(String message, ExpectedExceptionBlock<T> block) { 
    try { 
     block.exceptionThrowingCode(); 
     fail(message); 
    } catch (T ex) { 
    } 
    } 
} 

Sin embargo, Java no puede utilizar tipos de excepciones genéricas en un bloque catch, creo.

¿Cómo puedo hacer algo como esto, trabajando en torno a la limitación de Java?

¿Hay alguna manera de comprobar que la variable ex es del tipo T?

+0

Parece que tiene razón y * no * puede detectar genéricos. Lame http://stackoverflow.com/questions/2444633/why-doesnt-java-support-generic-throwables – rogerdpack

+0

Dangit, Java.Este es simplemente otro de los muchos obstáculos que se interponen en nuestro camino al tratar de hacer que el código sea lo más limpio posible utilizando las lambdas de Java 8. –

Respuesta

25

Puede pasar el objeto Class y comprobarlo mediante programación.

public static <T extends Exception> void checkForException(String message, 
     Class<T> exceptionType, ExpectedExceptionBlock<T> block) { 
    try { 
     block.exceptionThrowingCode(); 
    } catch (Exception ex) { 
     if (exceptionType.isInstance(ex)) { 
      return; 
     } else { 
      throw ex; //optional? 
     } 
    } 
    fail(message); 
} 

//... 
checkForException("Expected an NPE", NullPointerException.class, //... 

No estoy seguro de si desea el reestreno o no; reiniciar fallaría/error igualmente la prueba, pero semánticamente no lo haría, ya que básicamente significa "no obtuvimos la excepción que esperábamos" y eso representa un error de programación, en lugar de un error en el entorno de prueba.

+0

¡Probablemente deberías incluir un ejemplo de uso completo para que las personas puedan ver que no es más sencillo o más legible que el idioma estándar! –

+0

@Pete Kirkham: 'isAssignableFrom' toma una clase como parámetro, y esencialmente hace lo mismo que hace' isInstance' en un objeto. 'isInstance' devuelve verdadero si el argumento es compatible con la asignación de la clase, por lo que funciona igual en las subclases. –

+0

@ Kevin Bourillion: Estoy más interesado en las posibilidades técnicas que en abogar de una manera u otra. Creo que su publicación aborda correctamente los peligros de dicho enfoque, y ya lo votó en consecuencia. –

0

Bueno, podría simplemente coger Excepción y volver a lanzar si no es una Excepción esperada. Aunque la buena práctica de codificación generalmente dicta que la ruta de éxito del código no debe definirse por una excepción, entonces es posible que desee reconsiderar su diseño.

+0

Creo que para propósitos de prueba podría tener valor. –

+0

La respuesta de MarkPeters es más o menos lo que estaba buscando. Aunque todavía tiene toda la "ruta de ejecución exitosa esperada requerida requiere excepciones, mientras que el caso de error no lo hace". También cambia ligeramente la ejecución, por lo que las excepciones inesperadas no se vuelven a lanzar, sino que activan una llamada para que falle. En su código original, tenía tres rutas, una excepción esperada, una excepción inesperada que sube la pila, y ninguna excepción, la llamada falla. El código de marca simplifica a la excepción esperada y la llamada falla para las excepciones inesperadas y no hay excepciones. – ShadowRanger

+0

Disculpa, comenzó mi comentario antes de agregar el tuyo. Existe un uso potencial en las pruebas, pero quería advertirlo solo en caso de que fuera por código de producción. El manejo moderno de excepciones se optimiza para el caso de no excepción, por lo que bajo circunstancias de éxito incurrirá en gastos generales que su caso de falla evitará. Perverso, por decir lo menos. – ShadowRanger

5

Entiendo el impulso de intentar simplificar su idioma de excepción, pero en serio: no. Cada posible elección que se te ocurra es una cura peor que la enfermedad. Especialmente ¡Excepción de @ExpectedException de JUnit 4! Es una solución framework demasiado inteligente, que requiere que todos aprendan cómo funciona, a diferencia de un simple código evidente de código Java normal. Peor aún, no le da ninguna forma de ajustar solo la parte de su prueba que espera arrojar la excepción, por lo que si un paso de configuración anterior arroja la misma excepción, su prueba pasará aunque su código esté roto.

Podría escribir una larga diatriba sobre esto aquí (lo siento por no tener suficiente tiempo), ya que hemos tenido una larga discusión de este problema entre los ingenieros de Java aquí en Google, y el consenso fue que ninguno de estas soluciones locas valen la pena. Acostúmbrate a probar/atrapar, realmente no es tan malo.

+4

Quizás estoy equivocado, pero creo que JUnit es bastante específico en su diseño y que dentro de él, @ExpectedException no es una solución demasiado inteligente. Si sus pruebas son tan complicadas que no sabe de dónde podrían venir sus excepciones, entonces no está utilizando JUnit correctamente. Dirían que es posible que desee revisar la refacturación de su código para definir mejor qué están haciendo cada una de sus clases/métodos. Dado un buen diseño modular, no veo por qué esto debería ser un problema. ¿Hay algún problema que me falta o subestima? –

1

Generics no son tipos. No son plantillas. Son comprobaciones de tipo de tiempo de compilación, en Java. Los bloques de excepciones detectan el tipo. Puede atrapar (Excepción e) o incluso atrapar (Lanzable e) y luego lanzarlo según sea necesario.

Cuestiones relacionadas