Melhor maneira de verificar se um determinado tipo de exceção foi a causa (de uma causa, etc ...) em uma exceção aninhada?
-
03-07-2019 - |
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!
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
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);