Capturando erros corretamente: como testar exceções no JUnit em 2025

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: "Por que meu teste falha quando deveria passar?"

\n\n

Você já escreveu um teste unitário que deveria capturar uma exceção, mas ele falhou com um erro vermelho? Familiar? A pergunta "How do you assert that a certain exception is thrown in JUnit tests?" no Stack Overflow acumulou milhares de visualizações e centenas de comentários. E não é por acaso: testar exceções é um daqueles tópicos onde até desenvolvedores experientes às vezes tropeçam.

\n\n

Em 2025, quando a qualidade do código se tornou um fator crítico (especialmente em microsserviços e pipelines de IA), a habilidade de verificar exceções corretamente não é apenas "boa prática", mas uma competência básica. Neste artigo, vamos analisar todas as maneiras atuais de assert exceções no JUnit 5, mostrar as armadilhas e fornecer uma checklist de melhores práticas. Vamos lá!

\n\n

Por que é importante testar exceções?

\n\n

Imagine: você escreve o método divide(int a, int b). Se b == 0, o método deve lançar ArithmeticException. Sem um teste para esse comportamento, você corre o risco de que, em um mês, outro desenvolvedor (ou você mesmo) remova acidentalmente a verificação, e a aplicação falhe em produção com um erro incompreensível.

\n\n

Testes de exceções são o seu seguro. Eles documentam o comportamento esperado do sistema em casos limite. E o JUnit 5 oferece várias ferramentas elegantes para isso.

\n\n

Método 1: assertThrows — o rei do teste moderno

\n\n

A partir do JUnit 5 (e seu pacote org.junit.jupiter.api), o método assertThrows se tornou o padrão de facto. Por quê? Porque é conciso, legível e funcional.

\n\n
import static org.junit.jupiter.api.Assertions.assertThrows;\n\n@Test\nvoid whenDivideByZero_thenArithmeticException() {\n    assertThrows(ArithmeticException.class, \n        () -> calculator.divide(10, 0));\n}\n
\n\n

O que acontece aqui? assertThrows recebe a classe da exceção esperada e uma lambda com o código que deve lançá-la. Se a exceção for lançada, o teste fica verde. Caso contrário, fica vermelho. Simples e claro.

\n\n

Versão avançada: verificando a mensagem da exceção

\n\n

Às vezes, não basta verificar o tipo da exceção — é preciso garantir que a mensagem contenha o texto correto. assertThrows retorna o objeto da exceção, e você pode verificar suas propriedades:

\n\n
import static org.junit.jupiter.api.Assertions.*;\n\n@Test\nvoid whenInvalidEmail_thenExceptionWithMessage() {\n    IllegalArgumentException exception = assertThrows(\n        IllegalArgumentException.class,\n        () -> userService.register("invalid-email")\n    );\n    \n    assertTrue(exception.getMessage().contains("Email inválido"));\n}\n
\n\n

Essa abordagem é especialmente útil ao testar exceções personalizadas com campos (por exemplo, código de erro).

\n\n

Método 2: O bom e velho expected (legado do JUnit 4)

\n\n

Se você trabalha com JUnit 4 ou mantém código legado, provavelmente já viu o atributo expected na anotação @Test:

\n\n
@Test(expected = ArithmeticException.class)\npublic void whenDivideByZero_thenException() {\n    calculator.divide(10, 0);\n}\n
\n\n

Desvantagem: essa abordagem não permite verificar a mensagem da exceção ou suas propriedades. Além disso, se a exceção for lançada antes do esperado (por exemplo, na linha de inicialização), o teste pode passar falsamente. Em 2025, recomendamos evitar essa abordagem em código novo.

\n\n

Método 3: Try-catch com flag (para fãs de controle)

\n\n

Antes do JUnit 5, muitos usavam a construção com try-catch e uma flag booleana. Parece verboso, mas oferece controle total:

\n\n
@Test\nvoid whenDivideByZero_thenExceptionCaught() {\n    boolean thrown = false;\n    try {\n        calculator.divide(10, 0);\n    } catch (Arithmeti                

Blogs

Book Recommendations