¿La mejor forma de verificar si un determinado tipo de excepción fue la causa (de una causa, etc.) en una excepción anidada?

StackOverflow https://stackoverflow.com/questions/610628

Pregunta

Estoy escribiendo algunas pruebas de JUnit que verifican que se produce una excepción de tipo MyCustomException . Sin embargo, esta excepción se incluye en otras excepciones varias veces, por ejemplo, en una InvocationTargetException, que a su vez está envuelta en una RuntimeException.

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


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 a getCause () a algunas " capas " profundos, y similares rondas feos. ¿Hay alguna manera más agradable?

(Aparentemente, Spring tiene NestedRuntimeException.contains (Class) , que hace lo que quiero, pero no estoy usando Spring.)

CERRADO: Bien, supongo que realmente no hay manera de evitar un método de utilidad :-) ¡Gracias a todos los que respondieron!

¿Fue útil?

Solución

¿Por qué querría evitar getCause . Por supuesto, puedes escribirte un método para realizar la tarea, algo como:

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

Otros consejos

Si está utilizando Apache Commons Lang , entonces puedes usar lo siguiente:

(1) Cuando la causa debe ser exactamente del tipo especificado

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

(2) Cuando la causa debe ser del tipo especificado o su tipo de subclase

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

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

La imitación es la forma más sincera de adulación. Basado en una inspección rápida de La fuente , esto es exactamente lo que hace NestedRuntimeException:

/**
 * 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;
    }
}

CAVEAT : el código anterior es el 4 de marzo de 2009, por lo tanto, si realmente quiere saber qué está haciendo Spring en este momento, debe investigar el código tal como existe hoy (siempre que sea ).

Bueno, creo que no hay manera de hacer esto sin llamar a getCause () . Crees que es feo implementar una clase 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
     }
}

Puedes hacerlo usando guava:

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

Basado en la respuesta de Patrick Boos: Si usas Apache Commons Lang 3 puedes verificar:

indexOfThrowable : devuelve el índice (basado en cero) del primer Throwable que coincide con la clase especificada ( exactamente ) en la cadena de excepciones. Las subclases de la clase especificada no coinciden

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

o

indexOfType : devuelve el índice (basado en cero) del primer Throwable que coincide con la clase o subclase especificada en la cadena de excepciones. Las subclases de la clase especificada coinciden

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

Ejemplo para varios tipos con Java 8:

Class<? extends Throwable>[] classes = {...}
boolean match = Arrays.stream(classes)
            .anyMatch(clazz -> ExceptionUtils.indexOfType(e, clazz) != -1);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top