Лучший способ проверить, является ли определенный тип исключения причиной (причины и т. Д.) В вложенном исключении?
Я пишу некоторые тесты 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 (Class), который делает то, что я хочу - но я не использую Spring.)
ЗАКРЫТО: ОК, я думаю, что на самом деле обойтись без утилитарного метода:-) Спасибо всем, кто ответил!
9 ответов
Почему вы хотите избежать getCause
, Конечно, вы можете написать себе метод для выполнения задачи, например:
public static boolean isCause(
Class<? extends Throwable> expected,
Throwable exc
) {
return expected.isInstance(exc) || (
exc != null && isCause(expected, exc.getCause())
);
}
Если вы используете 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
}
Я не думаю, что у вас есть выбор, кроме как вызвать через слои getCause. Если вы посмотрите на исходный код исключительной ситуации Spring NestedRuntimeException, о которой вы упомянули, это то, как она реализована.
Основываясь на ответе Патрика Бооса: Если вы используете 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);
Вы можете сделать это с помощью гуавы:
FluentIterable.from(Throwables.getCausalChain(e))
.filter(Predicates.instanceOf(ConstraintViolationException.class))
.first()
.isPresent();
Подражание - это самая искренняя форма лести. Основываясь на быстрой проверке источника, именно это и делает 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
}
}
Если интересующее исключение определенно является "основной" причиной,
assertj
это дополнительное место, чтобы найти
getRootCause
check, хотя из источника сегодня, похоже, возможная проблема с бесконечным циклом обсуждается в других ответах.