2009-03-04 7 views
15

Piszę niektóre testy JUnit, które sprawdzają, czy zgłoszony został wyjątek typu MyCustomException. Jednak ten wyjątek jest wielokrotnie zawijany w innych wyjątkach, np. w wyjątku InvocationTargetException, który z kolei jest zawijany w wyjątek RuntimeException.Najlepszy sposób sprawdzenia, czy określony wyjątek był przyczyną (przyczyny, itp.) W zagnieżdżonym wyjątku?

Jaki jest najlepszy sposób ustalenia, czy MyCustomException w jakiś sposób spowodował wyjątek, który faktycznie złapałem? Chciałbym zrobić coś takiego (patrz podkreślone):


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

Chciałbym uniknąć wywoływania getCause() kilka „warstw” głębokie i podobnych brzydkie obejścia. Czy jest lepszy sposób?

(Najwyraźniej wiosna NestedRuntimeException.contains(Class), który robi to, co chcę - ale nie używam Wiosna.)

zamknięty: OK, myślę, że naprawdę nie ma sposobu poruszania użytkowego :-) Dziękuję wszystkim, którzy odpowiedzieli!

Odpowiedz

21

Dlaczego chcesz uniknąć getCause. Można, oczywiście, napisać własne metody, aby wykonać zadanie, coś jak:

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

Zauważ, że ten algorytm może spowodować nieskończoną pętlę jeśli przyczyna ma pętlę, która może się zdarzyć niektóre przypadki, takie jak wyjątki EclipseLink DB. [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)) obsługuje ten przypadek, więc prawdopodobnie 'indexOfThrowable' z odpowiedzi Patricka również. – DavidS

+0

@DavidS Nie jest to nieskończona pętla - wystarczy rzucić 'StackOverflowError'. Jeśli masz taki związek przyczynowy, masz większe problemy (prawdopodobnie próbujesz ponownie użyć wyjątków, mimo że są dziwnie zmienne). –

+1

A StackOverflowError następnie, dzięki. Bez względu na to, nie jest to problem z kodem, który napisałem; jest to problem w niektórych popularnych bibliotekach, w tym niektórych sterownikach jdbc Oracle. Jest to dość powszechny problem, który Apache Commons zdecydował się obsłużyć w 'getRootCause', Oracle zdecydował się obsłużyć go w [printStackTrace] (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6962571) i Guava za rozpatrzenie go w [Throwables] (https://github.com/google/guava/issues/1173). Mój komentarz miał na celu ostrzec o tym, nie polecam, jak najlepiej konstruować Wyjątki. – DavidS

5

Nie sądzę, że masz wybór, ale musisz zadzwonić przez warstwę getCause. Jeśli spojrzysz na kod źródłowy Spring NestedRuntimeException, o którym wspomniałeś, to w jaki sposób jest zaimplementowany.

1

Cóż, myślę, że nie ma sposobu, aby to zrobić bez wywoływania getCause(). To myślisz, że to brzydkie wdrożyć użytkową klasę dla tej operacji:

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

Naśladownictwo jest najszczerszą formą pochlebstwa. Based on a quick inspection of the source, to jest dokładnie to, co NestedRuntimeException robi:

/** 
* 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: Powyższe jest kod z dniem 4 marca 2009 roku tak, jeśli naprawdę chcesz wiedzieć, co wiosna robi teraz, należy badania kodu jak istnieje dzisiaj (kiedykolwiek to jest).

1

Można to zrobić przy użyciu guawa:

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

Jeśli używasz Apache Commons Lang, można użyć następujących:

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

podstawie Patrick Boos odpowiedź: Jeśli używasz Apache Commons Lang 3 można sprawdzić:

indexOfThrowable: Zwraca (bazujący na zera) ind ex pierwszego Throwable, który pasuje do określonej klasy (dokładnie) w łańcuchu wyjątków.podklas określonej klasy nie pasują

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

lub

indexOfType: Zwraca (zero based) indeks pierwszego Throwable który odpowiada określonej klasy lub podklasy w łańcuchu wyjątków . podklas określonej klasy pasują

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

Przykład dla wielu typów z Java 8:

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