2009-03-04 5 views
15

J'écris des tests JUnit qui vérifient qu'une exception de type MyCustomException est levée. Cependant, cette exception est enveloppée dans d'autres exceptions un certain nombre de fois, par ex. dans une exception InvocationTargetException, qui à son tour est enveloppée dans une exception RuntimeException.La meilleure façon de vérifier si un certain type d'exception était la cause (d'une cause, etc ...) dans une exception imbriquée?

Quelle est la meilleure façon de déterminer si MyCustomException a provoqué l'exception que j'ai réellement détectée? Je voudrais faire quelque chose comme ceci (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 » profondes, et similaires contournements laid. Y a-t-il une meilleure façon?

(Apparemment, le printemps est NestedRuntimeException.contains(Class), qui fait ce que je veux - mais je ne suis pas avec Spring.)

FERMÉ: OK, je suppose qu'il n'y a vraiment pas de se déplacer d'une méthode utilitaire :-) Merci à tous ceux qui ont répondu!

Répondre

21

Pourquoi voudriez-vous éviter getCause. Vous pouvez, bien sûr, vous écrire une méthode pour effectuer la tâche, quelque chose comme:

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

Notez que cet algorithme peut provoquer une boucle infinie si la cause est une boucle, ce qui peut se produire dans certains cas comme les exceptions DB EclipseLink. [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)) gère ce cas, donc probablement 'indexOfThrowable' de la réponse de Patrick le fait aussi. – DavidS

+0

@DavidS Pas une boucle infinie - elle lancerait rapidement 'StackOverflowError'. Si vous avez une rupture de causalité comme ça alors vous avez des problèmes plus importants (essayant probablement de réutiliser des objets d'exception même s'ils sont étrangement mutables). –

+1

Un StackOverflowError alors, merci. Quoi qu'il en soit, ce n'est pas un problème avec le code que j'ai écrit; C'est un problème dans certaines bibliothèques communes, y compris certains pilotes Oracle JDBC. C'est un problème assez commun qu'Apache Commons a choisi de gérer dans 'getRootCause', Oracle a choisi de le gérer dans [printStackTrace] (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6962571), et Gava envisagé de le traiter dans [Throwables] (https://github.com/google/guava/issues/1173). Mon commentaire était destiné à avertir de cela, ne recommande pas la meilleure façon de construire des exceptions. – DavidS

5

Je ne pense pas que vous ayez d'autre choix que d'appeler les couches de getCause. Si vous regardez le code source de Spring NestedRuntimeException que vous mentionnez, c'est comme cela qu'il est implémenté.

1

Eh bien, je pense qu'il n'y a aucun moyen de le faire sans appeler getCause(). Il vous pensez qu'il est laid mettre en œuvre une classe utilitaire pour ce faire:

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

L'imitation est la forme la plus sincère de flatterie. Based on a quick inspection of the source, c'est exactement ce NestedRuntimeException fait:

/** 
* 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: Ce qui précède est le code du 4 Mars 2009 afin, si vous voulez vraiment savoir ce printemps est en train de faire maintenant, vous devez rechercher le code tel qu'il existe aujourd'hui (chaque fois que c'est).

1

Vous pouvez le faire en utilisant la goyave:

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

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

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

Basé sur la réponse Patrick Boos: Si vous utilisez Apache Commons Lang 3 vous pouvez vérifier:

indexOfThrowable: Renvoie l'indice (basé sur zéro) ex du premier Throwable qui correspond à la classe spécifiée (exactement) dans la chaîne d'exception.Sous-classes de la classe spécifiée ne correspondent pas

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

ou

indexOfType: Renvoie la (base zéro) index du premier Throwable qui correspond à la classe spécifiée ou sous-classe dans la chaîne d'exception . Sous-classes de la classe spécifiée ne 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); 
Questions connexes