特定の例外タイプがネストされた例外の原因(原因など…)であったかどうかを確認する最良の方法は?
-
03-07-2019 - |
質問
MyCustomException
型の例外がスローされることを確認するJUnitテストをいくつか書いています。ただし、この例外は他の例外に何度もラップされます。 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()
をいくつかの" layers"と呼ぶことを避けたい深く、同様のい回避策。もっと良い方法はありますか?
(明らかに、Springには NestedRuntimeException.contains(Class)、これは私が望むことをします-しかし、私はSpringを使用していません。)
閉鎖: わかりました、ユーティリティメソッドを回避することは実際にはありません:-)答えてくれたすべての人に感謝します!
解決
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のソースコードを見ると、それがどのように実装されているか
模倣は誠実なお世辞です。 の迅速な検査に基づいてソース、これはまさに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;
}
}
警告:上記は2009年3月4日時点のコードです。したがって、Springが現在何をしているかを本当に知りたい場合は、現在のコードを調査する必要があります(いつでも)。
まあ、 getCause()
を呼び出さずにこれを行う方法はないと思います。これを行うためのユーティリティクラスの実装は見苦しいと思われます:
public class ExceptionUtils {
public static boolean wasCausedBy(Throwable e, Class<? extends Throwable>) {
// call getCause() until it returns null or finds the exception
}
}
guavaを使用してこれを行うことができます:
FluentIterable.from(Throwables.getCausalChain(e))
.filter(Predicates.instanceOf(ConstraintViolationException.class))
.first()
.isPresent();
Patrick Boosの回答に基づく: Apache Commons Lang 3を使用している場合は、次を確認できます。
indexOfThrowable :例外チェーン内の指定されたクラス( exactly )に一致する最初の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);