2009-03-04 4 views
15

Ich schreibe einige JUnit-Tests, die überprüfen, dass eine Ausnahme vom Typ MyCustomException ausgelöst wird. Diese Ausnahme wird jedoch einige Male in andere Ausnahmen eingeschlossen, z. in einer InvocationTargetException, die wiederum in eine RuntimeException eingeschlossen ist.Die beste Möglichkeit, um zu überprüfen, ob ein bestimmter Ausnahmetyp in einer verschachtelten Ausnahme die Ursache (einer Ursache usw.) war?

Was ist der beste Weg zu bestimmen, ob MyCustomException irgendwie die Ausnahme verursachte, die ich tatsächlich erwische? Ich möchte so etwas tun (siehe unterstrichene):


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

Ich möchte getCause() ein paar „Schichten“ tief und ähnliche hässliche Umgehungen vermeiden, fordern. Gibt es einen schöneren Weg?

(Offenbar Frühling NestedRuntimeException.contains(Class) hat, das tut, was ich will - aber ich bin mit Spring nicht.)

GESCHLOSSEN: OK, ich denke, es gibt wirklich keinen um eine Hilfsmethode bekommen :-) Danke an alle, die geantwortet haben!

Antwort

21

Warum möchten Sie vermeiden, getCause. Sie können natürlich, schreiben Sie sich ein Verfahren um die Aufgabe auszuführen, so etwas wie:

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

Beachten Sie, dass dieser Algorithmus eine unendliche Schleife verursachen kann, wenn die Ursache eine Schleife hat, die passieren kann, einige Fälle wie EclipseLink DB Ausnahmen. [Apache Commons Lang ExceptionUtils :: getRootCause] (https://commons.apache.org/proper/common-lang/javadocs/api-3.1/org/apache/commons/lang3/exception/ExceptionUtils.html#getRootCause (Java. lang.Throwable)) handhabt diesen Fall, also wahrscheinlich auch 'indexOfThrowable' aus Patricks Antwort. – DavidS

+0

@DavidS Keine Endlosschleife - es würde schnell "StackOverflowError" werfen. Wenn Sie einen solchen Kausalzusammenbruch haben, dann haben Sie größere Probleme (wahrscheinlich versuchen Sie, Ausnahmeobjekte wiederzuverwenden, obwohl sie seltsam veränderbar sind). –

+1

Ein StackOverflowError dann, danke. Egal, das ist kein Problem mit Code, den ich geschrieben habe; Es ist ein Problem in einigen allgemeinen Bibliotheken, einschließlich einiger Oracle jdbc-Treiber. Es ist ein häufig genug auftretendes Problem, dass Apache Commons es in 'getRootCause' gehandhabt hat, Oracle entschied sich dafür, es in [printStackTrace] (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6962571) und Guava zu handhaben überlegt, es in [Throwables] zu behandeln (https://github.com/google/guava/issues/1173). Mein Kommentar sollte davor warnen, nicht empfehlen, wie man Exceptions am besten baut. – DavidS

5

Ich glaube nicht, dass Sie keine andere Wahl haben, als die Schichten von getCause aufzurufen. Wenn Sie sich den Quellcode für die Spring NestedRuntimeException ansehen, den Sie erwähnen, ist dies die Implementierung.

1

Nun, ich glaube, es gibt keinen Weg, diese getCause() ohne Aufruf zu tun. Es Sie denken, es ist hässlich ein Dienstprogramm Klasse implementieren, dies zu tun:

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

Nachahmung ist die aufrichtigste Form der Schmeichelei. Based on a quick inspection of the source, das ist genau das, was NestedRuntimeException tut:

/** 
* 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: Die oben ist der Code als vom 4. März 2009 so, wenn Sie wirklich wissen wollen, was Frühling gerade jetzt tun, sollten Sie den Code Forschung so wie es heute existiert (wann immer das ist).

1

Sie können diese mit Guave tun:

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

Wenn Sie Apache Commons Lang verwenden, dann können Sie mit dem folgenden:

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

Basierend auf Patrick Boos Antwort: Wenn Sie Apache Commons mit Lang 3 können Sie überprüfen:

indexOfThrowable: Gibt die (Null basierte) ind ex des ersten Throwable, der der angegebenen Klasse (genau) in der Ausnahmekette entspricht.Subklassen der angegebenen Klasse entsprechen nicht

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

oder

indexOfType: Liefert den (Null basierend) Index des ersten Throwable, die die angegebene Klasse oder Unterklasse in der Ausnahmekette Spiele . Subklassen der angegebenen Klasse passen

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

Beispiel für mehrere Typen mit Java 8:

Class<? extends Throwable>[] classes = {...} 
boolean match = Arrays.stream(classes) 
      .anyMatch(clazz -> ExceptionUtils.indexOfType(e, clazz) != -1); 
Verwandte Themen