Il modo migliore per verificare se un determinato tipo di eccezione era la causa (di una causa, ecc.) In un'eccezione nidificata?

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

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!

È stato utile?

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);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top