Лучший способ проверить, является ли определенный тип исключения причиной (причины и т. Д.) Во вложенном исключении? - PullRequest
22 голосов
/ 04 марта 2009

Я пишу некоторые тесты JUnit, которые проверяют, что выброшено исключение типа MyCustomException. Однако это исключение включается в другие исключения несколько раз, например в InvocationTargetException, который, в свою очередь, обернут в RuntimeException.

Как лучше всего определить, вызвало ли MyCustomException исключение, которое я на самом деле отлавливаю? Я хотел бы сделать что-то вроде этого (см. Подчеркнутый):


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

Я бы хотел не называть getCause() несколькими "слоями" и подобными уродливыми обходными путями. Есть ли лучший способ?

(Очевидно, у Spring есть NestedRuntimeException.contains (Class) , который делает то, что я хочу, но я не использую Spring.)

ЗАКРЫТО Хорошо, я думаю, что на самом деле нет никакого способа обойтись с утилитой :-) Спасибо всем, кто ответил!

Ответы [ 7 ]

27 голосов
/ 25 марта 2015

Если вы используете Apache Commons Lang , то вы можете использовать следующее:

(1) Когда причина должна быть точно указанного типа

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

(2) Когда причина должна быть либо указанного типа, либо типа его подкласса

if (ExceptionUtils.indexOfType(exception, ExpectedException.class) != -1) {
    // exception is or has a cause of type ExpectedException.class or its subclass
}
25 голосов
/ 04 марта 2009

Почему вы хотите избежать getCause. Конечно, вы можете написать себе метод для выполнения задачи, например:

public static boolean isCause(
    Class<? extends Throwable> expected,
    Throwable exc
) {
   return expected.isInstance(exc) || (
       exc != null && isCause(expected, exc.getCause())
   );
}
5 голосов
/ 04 марта 2009

Я не думаю, что у вас есть выбор, кроме как вызвать через слои getCause. Если вы посмотрите на исходный код исключительной ситуации Spring NestedRuntimeException, о которой вы упомянули, это то, как она реализована.

2 голосов
/ 04 марта 2009

Подражание - самая искренняя форма лести. Основываясь на быстрой проверке источника , именно это и делает 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 : Выше приведен код от 4 марта 2009 года, поэтому, если вы действительно хотите знать, что Spring делает сейчас, вам следует изучить код в том виде, в каком он существует сегодня (когда бы то ни было) .

1 голос
/ 18 июля 2017

На основании ответа Патрика Бооса: Если вы используете Apache Commons Lang 3, вы можете проверить:

indexOfThrowable : Возвращает (основанный на нуле) индекс первого Throwable, который соответствует указанному классу ( точно ) в цепочке исключений. Подклассы указанного класса не совпадают

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

или

indexOfType : Возвращает (основанный на нуле) индекс первого Throwable, который соответствует указанному классу или подклассу в цепочке исключений. Подклассы указанного класса совпадают

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

Пример для нескольких типов с Java 8:

Class<? extends Throwable>[] classes = {...}
boolean match = Arrays.stream(classes)
            .anyMatch(clazz -> ExceptionUtils.indexOfType(e, clazz) != -1);
1 голос
/ 21 июля 2014

Вы можете сделать это, используя guava:

FluentIterable.from(Throwables.getCausalChain(e))
                        .filter(Predicates.instanceOf(ConstraintViolationException.class))
                        .first()
                        .isPresent();
1 голос
/ 04 марта 2009

Ну, я думаю, что нет способа сделать это без вызова getCause(). Если вы думаете, что уродливо реализует класс утилита для этого:

public class ExceptionUtils {
     public static boolean wasCausedBy(Throwable e, Class<? extends Throwable>) {
         // call getCause() until it returns null or finds the exception
     }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...