Meilleure façon de vérifier si un certain type d’exception était la cause (d’une cause, etc.) dans une exception imbriquée?

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

Question

J'écris des tests JUnit qui vérifient qu'une exception de type MyCustomException est levée. Cependant, cette exception est encapsulée dans d'autres exceptions plusieurs fois, par exemple. dans une InvocationTargetException, qui est à son tour encapsulée dans une RuntimeException.

Quel est le meilleur moyen de déterminer si MyCustomException a provoqué l’exception que j’ai réellement capturée? Je voudrais faire quelque chose comme ça (voir souligné):


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

Je voudrais éviter d'appeler getCause () quelques "couches". des solutions de rechange profondes et similaires. Y a-t-il un moyen plus agréable?

(Apparemment, Spring a NestedRuntimeException.contains (Class) , qui fait ce que je veux - mais je n'utilise pas Spring.)

FERMÉ: OK, je suppose qu’il n’ya vraiment pas moyen de contourner une méthode utilitaire :-) Merci à tous ceux qui ont répondu!

Était-ce utile?

La solution

Pourquoi voudriez-vous éviter getCause . Vous pouvez bien sûr écrire vous-même une méthode pour effectuer la tâche, par exemple:

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

Autres conseils

Si vous utilisez Apache Commons Lang , vous pouvez utiliser les éléments suivants:

(1) Quand la cause doit être exactement du type spécifié

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

(2) Quand la cause doit être du type spécifié ou du type de sa sous-classe

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

Je ne pense pas que vous ayez d'autre choix que d'appeler à travers les couches de getCause. Si vous examinez le code source de l’exception Spring NestedRuntimeException, c’est ainsi qu’il est implémenté.

L’imitation est la plus sincère des flatteries. Sur la base d'un examen rapide de la source , c’est exactement ce que fait 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 : Le code ci-dessus est le code en date du 4 mars 2009. Si vous voulez vraiment savoir ce que Spring fait actuellement, vous devez rechercher le code tel qu'il existe aujourd'hui (le cas échéant). ).

Eh bien, je pense qu’il est impossible de le faire sans appeler getCause () . Si vous pensez que c'est moche, implémentez une classe utilitaire pour cela:

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

Vous pouvez le faire en utilisant la goyave:

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

D'après la réponse de Patrick Boos: Si vous utilisez Apache Commons Lang 3, vous pouvez vérifier:

indexOfThrowable : renvoie l'index (basé sur zéro) du premier jetable qui correspond à la classe spécifiée ( exactement ) dans la chaîne des exceptions. Les sous-classes de la classe spécifiée ne correspondent pas

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

ou

indexOfType : retourne l'index (basé sur zéro) du premier Throwable qui correspond à la classe ou sous-classe spécifiée dans la chaîne des exceptions. Les sous-classes de la classe spécifiée correspondent

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

Exemple pour plusieurs types avec Java 8:

Class<? extends Throwable>[] classes = {...}
boolean match = Arrays.stream(classes)
            .anyMatch(clazz -> ExceptionUtils.indexOfType(e, clazz) != -1);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top