web-dev-qa-db-ja.com

特定の例外タイプがネストされた例外の原因(原因など)であるかどうかを確認する最良の方法は?

タイプ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()をいくつかの「レイヤー」の深さで呼び出すことや、同様の醜い回避策を避けたいと思います。より良い方法はありますか?

(どうやら、Springには NestedRuntimeException.contains(Class) があり、これは私が望むことを実行します-しかし、私はSpringを使用していません。)

CLOSED:OK、ユーティリティメソッドを回避することは実際にはないと思います:-)返信してくれたすべての人に感謝します!

24
Yang Meyer

なぜgetCauseを避けたいのですか。もちろん、次のようなタスクを実行するためのメソッドを自分で作成することもできます。

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

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
}
30
Patrick Boos

GetCauseのレイヤーを介して呼び出す以外に選択肢はないと思います。 Spring NestedRuntimeExceptionのソースコードを見ると、それが実装方法であると述べています。

5
Mark

模倣はお世辞の誠実な形です。 ソースのクイックインスペクションに基づく 、これはまさに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日現在のコードなので、本当に春を知りたい場合はが現在実行している場合は、現在存在するコードを調査する必要があります(その場合はいつでも)。

2
Bob Cross

そうですね、getCause()を呼び出さずにこれを行う方法はないと思います。これを行うためにtilityクラスを実装するのは醜いと思います:

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

Patrick Boosの回答に基づく: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);
1
Nagy Attila

あなたはグアバを使ってこれをすることができます:

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