Melhor maneira de verificar se um determinado tipo de exceção foi a causa (de uma causa, etc ...) em uma exceção aninhada?

StackOverflow https://stackoverflow.com/questions/610628

Pergunta

Eu estou escrevendo alguns testes JUnit que verificam que uma exceção do tipo MyCustomException é lançada. No entanto, esta excepção é envolvida em outras excepções um número de vezes, por exemplo em uma InvocationTargetException, que por sua vez está envolto em um RuntimeException.

Qual é a melhor maneira de determinar se MyCustomException alguma forma causou a exceção que eu realmente pegar? Gostaria de fazer algo assim (veja sublinhado):


try {
    doSomethingPotentiallyExceptional();
    fail("Expected an exception.");
} catch (RuntimeException e) {
     if (!e.wasCausedBy(MyCustomException.class)
        fail("Expected a different kind of exception.");
}

Eu gostaria de evitar chamar getCause() algumas "camadas" de profundidade, e como contorná-las feias semelhantes. Existe uma maneira mais agradável?

(Aparentemente, Primavera tem NestedRuntimeException.contains (Classe) , que faz o que eu quero -. mas eu não estou usando Spring)

FECHADO: OK, eu acho que não há realmente nenhum começar em torno de um método utilitário :-) Obrigado a todos que responderam!

Foi útil?

Solução

Por que você quer evitar getCause. Você pode, é claro, escrever-se um método para executar a tarefa, algo como:

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

Outras dicas

Se você estiver usando Apache Commons Lang , então você pode usar o seguinte:

(1) Quando a causa deve ser exatamente do tipo especificado

if (ExceptionUtils.indexOfThrowable(exception, ExpectedException.class) != -1) {
    // exception is or has a cause of type ExpectedException.class
}

(2) Quando a causa deve ser tanto do tipo especificado, ou seu tipo subclasse

if (ExceptionUtils.indexOfType(exception, ExpectedException.class) != -1) {
    // exception is or has a cause of type ExpectedException.class or its subclass
}

Eu não acho que você tem outra escolha senão chamada através das camadas de getCause. Se você olhar para o código-fonte para o NestedRuntimeException Primavera que você menciona que é como ele é implementado.

A imitação é a forma mais sincera de lisonja. Baseado em uma rápida inspeção a fonte , este é exatamente o que NestedRuntimeException faz:

/**
 * 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 : A descrição acima é o código como de 4 de Março de 2009, para, se você realmente quer saber o que a primavera está fazendo agora, você deve pesquisar o código como existe hoje (sempre que é ).

Bem, eu acho que não há nenhuma maneira de fazer isso sem chamar getCause(). Ele acha que é feio implementar um utilitário classe para fazer isso:

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

Você pode fazer isso usando goiaba:

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

Com base na resposta Patrick Boos: Se você está usando Apache Commons Lang 3 você pode verificar:

indexOfThrowable : Retorna a (base zero) índice do primeiro Throwable que coincide com a classe especificada ( exatamente ) na cadeia de exceção. As subclasses da classe especificada não correspondem

if (ExceptionUtils.indexOfThrowable(e, clazz) != -1) {
    // your code
}

ou

indexOfType : Regressa o (com base zero) do índice da primeira Throwable que coincide com o especificado classe ou subclasse na cadeia de excepção. As subclasses da classe especificada fazer jogo

if (ExceptionUtils.indexOfType(e, clazz) != -1) {
    // your code
}

Exemplo de vários tipos com Java 8:

Class<? extends Throwable>[] classes = {...}
boolean match = Arrays.stream(classes)
            .anyMatch(clazz -> ExceptionUtils.indexOfType(e, clazz) != -1);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top