Лучший способ проверить, был ли определенный тип исключения причиной (причины и т.д.) Во вложенном исключении?
-
03-07-2019 - |
Вопрос
Я пишу несколько тестов 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);