Introdução

Neste post vou mostrar de forma sucinta e prática como testar exceções em Java, utilizando o framework JUnit 4. Vou demonstrar 3 formas de escrever testes unitários que verificam o comportamento de exceções.

Nossa classe para ser testada

As 3 formas de se testar exceções em Java serão feitas em cima da classe mostrada abaixo.

<pre class=“brush: java; title: ; notranslate”>public class TransferenciaBancariaService {

private TransferenciaBancariaRepository repository = new TransferenciaBancariaRepository();

public void transfere(ContaBancaria origem, ContaBancaria destino, BigDecimal valor) {
	validarTransferencia(destino, valor);
	origem.debita(valor);
	destino.deposita(valor);
	repository.salvar(origem);
	repository.salvar(destino);
}

private void validarTransferencia(ContaBancaria destino, BigDecimal valor) {
	if (destino.getSaldo().compareTo(valor) &amp;lt; 0) {
		throw new SaldoInsuficienteException(destino);
	}
}

}
</pre>

O código é simples. Faz a transferência de um valor entre uma conta origem e uma conta destino. Se o saldo da conta origem não for suficiente para a realização da transferência, uma exceção será lançada.

Abaixo o código da exceção.

<pre class=“brush: java; title: ; notranslate”>public class SaldoInsuficienteException extends RuntimeException {

public static final String SALDO_INSUFICIENTE_MSG = "A conta %s não possui saldo suficiente";

public SaldoInsuficienteException(ContaBancaria destino) {
	super(String.format(SALDO_INSUFICIENTE_MSG, destino.toString()));

}

}
</pre>

Vamos aos testes.

Como testar exceções em Java #1: @Test(expected=...)

O JUnit 4 permite definirmos, opcionalmente, como argumento da anotação @Test o expected.

O expected nos permite especificar uma exceção que esperamos que seja lançada pelo código sendo testado. O teste só tem sucesso se a exceção for lançada, caso contrário temos uma falha.

O exemplo abaixo mostra o código de teste com o expected.

<pre class=“brush: java; title: ; notranslate”>public class TransferenciaBancariaServiceTest {

private TransferenciaBancariaService service = new TransferenciaBancariaService();

@Test(expected = SaldoInsuficienteException.class)
public void transferenciaImpossivelSaldoInsuficienteDeveLancarExcecao() {
ContaBancaria origem = new ContaBancaria(“CONTA-A”, BigDecimal.valueOf(1500l));
ContaBancaria destino = new ContaBancaria(“CONTA-B”, BigDecimal.valueOf(500l));

service.transfere(origem, destino, BigDecimal.valueOf(2000l));

}

}
</pre>

O código de teste acima deve lançar uma exceção, pois o valor que desejamos transferir – 2000 – é maior que o saldo da conta origem: 1500.

O argumento expected=SaldoInsuficienteException.class diz ao JUnit que esperamos que o teste ao executar lance uma exceção do tipo SaldoInsuficienteException. Veja que não precisamos fazer asserções nesse código; o argumento expected de @Test faz esse papel.

Embora o teste anterior seja legal e bastante conciso, ele não atende a um ponto: e se quisermos verificar a mensagem que foi lançada? Nesse caso, vamos ter que usar uma abordagem diferente. Vamos a ela.

Como testar exceções em Java #2: blocos try {} catch()

Uma outra forma de como testar exceções em Java é utilizando um bloco try{} catch. Essa forma era bastante utilizada no JUnit 3, antes de aparecer a funcionalidade @Test(expected=...).

Embora essa forma de se testar exceções não seja mais tão utilizada, ela ainda é útil quando queremos verificar a mensagem de detalhe da exceção disparada. Isso ocorre, por exemplo, se a mensagem da exceção vai ser exibida na camada de apresentação para o usuário. Nesse caso, é legal validarmos se a mensagem é aquela que esperamos.

Vamos ao código de teste com try{} catch().

<pre class=“brush: java; highlight: [12,14]; title: ; notranslate”>public class TransferenciaBancariaServiceTest {

private TransferenciaBancariaService service = new TransferenciaBancariaService();

@Test
public void transferenciaImpossivelSaldoInsuficienteDeveLancarExcecao() {
ContaBancaria origem = new ContaBancaria(“CONTA-A”, BigDecimal.valueOf(1500l));
ContaBancaria destino = new ContaBancaria(“CONTA-B”, BigDecimal.valueOf(500l));

try {
  service.transfere(origem, destino, BigDecimal.valueOf(2000l));
  fail("Falha. Uma exceção deve ser lançada!");
} catch (SaldoInsuficienteException ex) {
  assertEquals(String.format(SaldoInsuficienteException.SALDO_INSUFICIENTE_MSG, origem),  ex.getMessage());

}
}

}
</pre>

A preparação e a condição de falha é a mesma do teste anterior. A diferença está na forma que verificamos a ocorrência da exceção.

Veja as duas linhas em destaque:

  • A primeira contém o Assert.fail(); se o código de teste executar essa linha, indica que ele falhou, pois, o teste deveria lançar uma exceção;
  • É no catch() que esperamos que a execução do teste vá. Na segunda linha em destaque, utilizamos o assertEquals para verificar se a mensagem da exceção (ex.getMessage()) é a que esperamos.

Esse código atende muito bem nossas expectativas. Mas não sei o que vocês acham, mas na minha opinião não é muito elegante utilizar try {} catch() para testar a ocorrência de uma exceção e validar sua mensagem.

Pois bem. O JUnit 4 tem uma forma mais elegante de resolver isso.

Como testar exceções em Java #3: @Rule e ExpectedException

O Junit 4.7 introduziu o conceito de Rules.

As Rules, de maneira geral, permitem adicionar comportamentos que serão executados antes e depois de cada método de teste. O JUnit já vem com algumas _test rules _predefinidas e permite, também, criarmos as nossas próprias Rules. Uma das test rules oferecidas pelo JUnit é a ExpectedException. É ela que vamos usar em nosso próximo exemplo.

Vamos mostrar o código.

<pre class=“brush: java; highlight: [5,6,13,14]; title: ; notranslate”>public class TransferenciaBancariaServiceTest {

private TransferenciaBancariaService service = new TransferenciaBancariaService();

@Rule
public ExpectedException excecaoEsperada = ExpectedException.none();

@Test
public void transferenciaImpossivelSaldoInsuficienteDeveLancarExcecao() {
ContaBancaria origem = new ContaBancaria(“CONTA-A”, BigDecimal.valueOf(1500l));
ContaBancaria destino = new ContaBancaria(“CONTA-B”, BigDecimal.valueOf(500l));

excecaoEsperada.expect(SaldoInsuficienteException.class);
excecaoEsperada.expectMessage(String.format(SaldoInsuficienteException.SALDO_INSUFICIENTE_MSG, origem));

service.transfere(origem, destino, BigDecimal.valueOf(2000l));

}

}
</pre>

Uma ExpectedException é uma rule que nos permite verificar se o nosso código lança uma determinada exceção. As linhas em destaque no trecho de código anterior fazem uso dessa rule:

  • As duas primeiras linhas destacadas mostram a declaração da rule. Para isso, utilizamos a anotação @Rule e a classe ExpectedException. Como se pode ver, a varíavel excecaoEsperada é inicializada com o o valor ExpectedException.none(); essa inicialização é para informar que, por padrão, nenhuma exceção é esperada.
  • A próxima linha em destaque – excecaoEsperada.expect() – modifica o comportamento padrão definido anteriormente, informando qual o tipo de exceção esperamos: SaldoInsuficienteException;
  • A última linha destacada nos permite verificar a mensagem da exceção (excecaoEsperada.expectMessage).

Cabe ressaltar algumas coisas sobre os test rules (variáveis anotadas com @Rule) para que elas funcionem adequadamente:

  1. A variável deve ser pública;
  2. A variável não pode ser estática (static);
  3. A variável deve ser um subtipo de TestRule.

Se quiser saber um pouco mais sobre as test rules recomendo a leitura do excelente artigo Testes isolados com jUnit Rules do Rafael Ponte.

Conclusão

Apresentei, neste post, 3 formas diferentes de como testar exceções em Java utilizando o framework JUnit.

Eu, pessoalmente gosto da primeira e terceira formas, pois o código fica mais limpo com as mesmas. Utilizo a primeira quando não preciso verificar a mensagem da exceção. A última, por sua vez, utilizo quando preciso garantir que a mensagem de exceção é a esperada.

Todo o código desse post pode ser encontrado nesse link do meu GitHub.

Abraços e até a próxima.

O post Como testar exceções em Java com o JUnit apareceu primeiro em Code a Test.

http://www.codeatest.com/como-testar-excecoes-em-java-junit/