Il modo migliore per verificare se un determinato tipo di eccezione era la causa (di una causa, ecc.) In un'eccezione nidificata?
-
03-07-2019 - |
Domanda
Sto scrivendo alcuni test JUnit che verificano che venga generata un'eccezione di tipo MyCustomException
. Tuttavia, questa eccezione è racchiusa in altre eccezioni un numero di volte, ad es. in un InvocationTargetException, che a sua volta è racchiuso in un RuntimeException.
Qual è il modo migliore per determinare se MyCustomException ha causato in qualche modo l'eccezione che ho effettivamente catturato? Vorrei fare una cosa del genere (vedi sottolineato):
try { doSomethingPotentiallyExceptional(); fail("Expected an exception."); } catch (RuntimeException e) { if (!e.
wasCausedBy(MyCustomException.class) fail("Expected a different kind of exception."); }
Vorrei evitare di chiamare getCause ()
alcuni "strati" profonde e simili brutte soluzioni. C'è un modo migliore?
(Apparentemente, Spring ha NestedRuntimeException.contains (Class) , che fa quello che voglio, ma non sto usando Spring.)
CHIUSO: OK, suppongo che non ci sia davvero modo di aggirare un metodo di utilità :-) Grazie a tutti coloro che hanno risposto!
Soluzione
Perché dovresti evitare getCause
. Ovviamente puoi scriverti un metodo per eseguire l'attività, qualcosa del tipo:
public static boolean isCause(
Class<? extends Throwable> expected,
Throwable exc
) {
return expected.isInstance(exc) || (
exc != null && isCause(expected, exc.getCause())
);
}
Altri suggerimenti
Se si utilizza Apache Commons Lang , quindi puoi utilizzare quanto segue:
(1) Quando la causa deve essere esattamente del tipo specificato
if (ExceptionUtils.indexOfThrowable(exception, ExpectedException.class) != -1) {
// exception is or has a cause of type ExpectedException.class
}
(2) Quando la causa dovrebbe essere del tipo specificato o del suo tipo di sottoclasse
if (ExceptionUtils.indexOfType(exception, ExpectedException.class) != -1) {
// exception is or has a cause of type ExpectedException.class or its subclass
}
Non penso che tu abbia altra scelta che chiamare i livelli di getCause. Se guardi il codice sorgente per Spring NestedRuntimeException che dici che è così che viene implementato.
L'imitazione è la forma più sincera di adulazione. Basato su una rapida ispezione di la fonte , questo è esattamente ciò che fa 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 : quanto sopra è il codice al 4 marzo 2009, quindi, se vuoi davvero sapere cosa sta facendo Spring in questo momento, dovresti cercare il codice così com'è oggi (ogni volta che è ).
Beh, penso che non ci sia modo di farlo senza chiamare getCause ()
. Pensi che sia brutto implementare una classe utility per fare questo:
public class ExceptionUtils {
public static boolean wasCausedBy(Throwable e, Class<? extends Throwable>) {
// call getCause() until it returns null or finds the exception
}
}
Puoi farlo usando guava:
FluentIterable.from(Throwables.getCausalChain(e))
.filter(Predicates.instanceOf(ConstraintViolationException.class))
.first()
.isPresent();
Basato sulla risposta di Patrick Boos: Se usi Apache Commons Lang 3 puoi controllare:
indexOfThrowable : restituisce l'indice (a base zero) del primo Throwable che corrisponde alla classe specificata ( esattamente ) nella catena delle eccezioni. Le sottoclassi della classe specificata non corrispondono
if (ExceptionUtils.indexOfThrowable(e, clazz) != -1) {
// your code
}
o
indexOfType : restituisce l'indice (a base zero) del primo Throwable che corrisponde alla classe o sottoclasse specificata nella catena delle eccezioni. Le sottoclassi della classe specificata corrispondono
if (ExceptionUtils.indexOfType(e, clazz) != -1) {
// your code
}
Esempio per più tipi con Java 8:
Class<? extends Throwable>[] classes = {...}
boolean match = Arrays.stream(classes)
.anyMatch(clazz -> ExceptionUtils.indexOfType(e, clazz) != -1);