Лучший способ проверить, был ли определенный тип исключения причиной (причины и т.д.) Во вложенном исключении?

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

Вопрос

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

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


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

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

(По-видимому, Весна имеет NestedRuntimeException.содержит(класс), который делает то, что я хочу, но я не использую Spring.)

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

Это было полезно?

Решение

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

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

Другие советы

Если вы используете Общий Язык Apache, тогда вы можете использовать следующее:

(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
}

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

Подражание - это самая искренняя форма лести. Основано на быстром ознакомлении с источником, это именно то , что делает 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;
    }
}

ПРЕДОСТЕРЕЖЕНИЕ:Приведенный выше код по состоянию на 4 марта 2009 года, поэтому, если вы действительно хотите знать, что Spring делает прямо сейчас, вам следует изучить код в том виде, в каком он существует сегодня (когда бы это ни было).

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

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

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

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

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

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

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

или

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

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);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top