Лучший способ проверить, был ли какой-то тип исключения причиной (причины и т.д.) Во вложенном исключении?
Я пишу несколько тестов 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()
нескольких "слоев" в глубину и подобных уродливых обходов. Есть ли лучший способ?
(По-видимому, Spring имеет NestedRuntimeException.contains(класс), который делает то, что я хочу - но я не использую Spring.)
ЗАКРЫТО:
Хорошо, я думаю, что на самом деле не существует способа утилиты:-) Спасибо всем, кто ответил!
Ответы
Ответ 1
Почему вы хотите избежать getCause
. Вы можете, конечно, написать себе способ выполнения задачи, например:
public static boolean isCause(
Class<? extends Throwable> expected,
Throwable exc
) {
return expected.isInstance(exc) || (
exc != null && isCause(expected, exc.getCause())
);
}
Ответ 2
Если вы используете Apache Commons Lang, вы можете использовать следующее:
if (ExceptionUtils.indexOfThrowable(exception, ExpectedException.class) != -1) {
// exception is or has a cause of type ExpectedException.class
}
Ответ 3
Я не думаю, что у вас есть выбор, кроме как вызвать слои getCause. Если вы посмотрите на исходный код для Spring NestedRuntimeException, о котором вы упоминаете, это то, как он реализован.
Ответ 4
Имитация - это самая искренняя форма лести. Основываясь на быстрой проверке источника, это именно то, что делает 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 прямо сейчас, вам следует исследовать код, как он существует сегодня (всякий раз то есть).
Ответ 5
Ну, я думаю, что нет способа сделать это без вызова getCause()
. Вы считаете, что уродливо реализовать класс утилиты для этого:
public class ExceptionUtils {
public static boolean wasCausedBy(Throwable e, Class<? extends Throwable>) {
// call getCause() until it returns null or finds the exception
}
}
Ответ 6
Вы можете сделать это с помощью guava:
FluentIterable.from(Throwables.getCausalChain(e))
.filter(Predicates.instanceOf(ConstraintViolationException.class))
.first()
.isPresent();
Ответ 7
На основании ответа Патрика Бооса:
Если вы используете 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);