2009-03-04 21 views
15

Estoy escribiendo algunas pruebas JUnit que verifican que se produce una excepción del tipo MyCustomException. Sin embargo, esta excepción se envuelve en otras excepciones varias veces, p. en una InvocationTargetException, que a su vez está envuelto en una RuntimeException.¿Cuál es la mejor manera de verificar si un determinado tipo de excepción fue la causa (de una causa, etc.) de una excepción anidada?

¿Cuál es la mejor manera de determinar si MyCustomException de alguna manera causó la excepción que realmente capté? Me gustaría hacer algo como esto (ver subrayada):


try { 
    doSomethingPotentiallyExceptional(); 
    fail("Expected an exception."); 
} catch (RuntimeException e) { 
    if (!e.wasCausedBy(MyCustomException.class) 
     fail("Expected a different kind of exception."); 
} 

me gustaría evitar llamar getCause() unos "capas" de profundidad, y similares feas soluciones temporales. ¿Hay una manera más agradable?

(Al parecer, la primavera ha NestedRuntimeException.contains(Class), que hace lo que quiero - pero no estoy usando la primavera.)

CERRADO: OK, supongo que no hay realmente ningún conseguir alrededor de un método de utilidad :-) Gracias a todos los que respondieron!

Respuesta

21

¿Por qué querría evitar getCause. Puede, por supuesto, escribir a sí mismo un método para realizar la tarea, algo así como:

public static boolean isCause(
    Class<? extends Throwable> expected, 
    Throwable exc 
) { 
    return expected.isInstance(exc) || (
     exc != null && isCause(expected, exc.getCause()) 
    ); 
} 
+1

Tenga en cuenta que este algoritmo puede provocar un bucle infinito si la causa tiene un bucle, que puede ocurrir en algunos casos como las excepciones de EclipseLink DB. [Apache Commons Lang ExceptionUtils :: getRootCause] (https://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/exception/ExceptionUtils.html#getRootCause (java. lang.Throwable)) maneja este caso, por lo que probablemente 'indexOfThrowable' de la respuesta de Patrick también lo haga. – DavidS

+0

@DavidS No es un bucle infinito - lanzaría rápidamente 'StackOverflowError'. Si tienes un colapso de causalidad así, entonces tienes problemas más grandes (probablemente intentando reutilizar objetos de excepción aunque sean extrañamente mutables). –

+1

A StackOverflowError, gracias. De todos modos, esto no es un problema con el código que he escrito; es un problema en algunas bibliotecas comunes, incluidos algunos controladores Oracle jdbc. Es un problema bastante común que Apache Commons decidió manejarlo en 'getRootCause', Oracle eligió manejarlo en [printStackTrace] (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6962571), y Guava considerado manejarlo en [Throwables] (https://github.com/google/guava/issues/1173). Mi comentario fue para advertir sobre esto, no recomendar la mejor manera de construir Excepciones. – DavidS

5

No creo que tenga otra opción que llamar a través de las capas de getCause. Si observa el código fuente de Spring NestedRuntimeException que menciona, así es como se implementa.

1

Bueno, creo que no hay manera de hacer esto sin tener que llamar getCause(). Es que creo que es feo implementar una clase de utilidad para hacer esto:

public class ExceptionUtils { 
    public static boolean wasCausedBy(Throwable e, Class<? extends Throwable>) { 
     // call getCause() until it returns null or finds the exception 
    } 
} 
2

La imitación es la forma más sincera de adulación. Based on a quick inspection of the source, esto es exactamente lo que NestedRuntimeException hace:

/** 
* Check whether this exception contains an exception of the given type: 
* either it is of the given class itself or it contains a nested cause 
* of the given type. 
* @param exType the exception type to look for 
* @return whether there is a nested exception of the specified type 
*/ 
public boolean contains(Class exType) { 
    if (exType == null) { 
     return false; 
    } 
    if (exType.isInstance(this)) { 
     return true; 
    } 
    Throwable cause = getCause(); 
    if (cause == this) { 
     return false; 
    } 
    if (cause instanceof NestedRuntimeException) { 
     return ((NestedRuntimeException) cause).contains(exType); 
    } 
    else { 
     while (cause != null) { 
      if (exType.isInstance(cause)) { 
       return true; 
      } 
      if (cause.getCause() == cause) { 
       break; 
      } 
      cause = cause.getCause(); 
     } 
     return false; 
    } 
} 

Claves clínicas: Lo anterior es el código como de 4 de marzo de 2009, para que, si realmente quiere saber lo que la primavera está haciendo en este momento, usted debe investigar el código como existe hoy (siempre que eso sea).

1

Usted puede hacer esto mediante la guayaba:

FluentIterable.from(Throwables.getCausalChain(e)) 
         .filter(Predicates.instanceOf(ConstraintViolationException.class)) 
         .first() 
         .isPresent(); 
16

Si está utilizando Apache Commons Lang, a continuación, puede utilizar el siguiente:

if (ExceptionUtils.indexOfThrowable(exception, ExpectedException.class) != -1) { 
    // exception is or has a cause of type ExpectedException.class 
} 
0

Sobre la base de Patrick Boos respuesta: Si el uso de Apache Commons Lang 3 puede verificar:

indexOfThrowable: Devuelve el (basado en cero) ind ex del primer Throwable que coincide con la clase especificada (exactamente) en la cadena de excepciones.Las subclases de la clase especificada no coincide con

if (ExceptionUtils.indexOfThrowable(e, clazz) != -1) { 
    // your code 
} 

o

indexOfType: Devuelve el (basado en cero) índice del primer Throwable que coincide con el clase o subclase específico de la cadena excepción . Las subclases de la clase especificada coinciden

if (ExceptionUtils.indexOfType(e, clazz) != -1) { 
    // your code 
} 

Ejemplo de varios tipos con Java 8:

Class<? extends Throwable>[] classes = {...} 
boolean match = Arrays.stream(classes) 
      .anyMatch(clazz -> ExceptionUtils.indexOfType(e, clazz) != -1); 
Cuestiones relacionadas