|
Criando Testes de Unidades com o Junit 4 usando anotações
No presente artigo irei fazer uma abordagem sobre a
versão do framework mais famoso para testes de unidade em plataforma Java e
as novidades encontradas na sua versão 4. Ressalto que não irei entrar nos
méritos de explicação do JUnit visto que o mesmo já se encontra presente em
diversos artigos publicados no Portal Java Magazine.
JUnit se tornou de fato um padrão de framework para
testes de unidades automatizados em plataforma Java. A
versão inicial do framework contemplava uma série de recursos para a
implementação de testes de unidade de forma fácil e flexível. A versão 4 é
considerada uma das mais significativas atualizações por ter como principal
proposta a simplificação da elaboração das classes de testes explorando os
recursos de anotação presente na JDK 1.5 do Java. O framework agora suporta
generics, enumerations, import estático e anotações – adição de metadados no
código para reduzir a complexidade de implementação e aumentar consideravelmente
o nível de reuso do código.
Mudanças nos Métodos de Teste Nas versões anteriores do Junit todas as classes de
teste eram escritas seguindo uma especificação que definia nomenclatura de
nomes e operações para o algoritmo de teste. Utilizando reflexão o framework
localizava todos os métodos de testes e os processava, como no trecho de
abaixo.
import junit.framework.TestCase; public class TesteUnidade extends TestCase { private int x = 1; private int y = 1; public void testMetodo() { int z = x + y; assertEquals(2, z); }
} Listagem 1 – Exemplo de código da versão anterior do
JUnit.
Em contraste, no JUnit 4 os métodos de teste são
identificados com a anotação @Test. Observe que agora não é preciso iniciar os
métodos de teste com a palavra test o que garante que os teste se aproximem
mais dos nomes de métodos implementados como apresentado no código abaixo.
import org.junit.Test; import junit.framework.TestCase; public class TesteUnidade extends TestCase { private int x = 1; private int y = 1; @Test public void metodo() { int z = x + y; assertEquals(2, z); }
} Listagem 2 – Exemplo de código JUnit 4.
Com o novo recurso é possível deixar os nomes dos
métodos das classes de teste similares aos métodos reais gerando uma
padronização de nomes. Por exemplo, Pessoa.getNome() é testado por PessoaTeste.getNome(); Lista.addAll() é testado
por ListTest.addAll().
A classe TestCase não foi descontinuada
para garantir compatibilidade reversa com as outras versões embora não seja
necessário a sua utilização. Com os recursos de import estático do java é
possível escrever uma classe de teste sem estender da classe TestCase e a mesma
permanecer com o mesmo formato como no exemplo abaixo
import static org.junit.Assert.assertEquals; public class TesteUnidade { private int x = 1; private int y = 1; @Test public void metodo() { int z = x + y; assertEquals(2, z); }
} Listagem 3 – Exemplo de código JUnit 4 sem estender de
TestCase
Setup e TearDown Na
versão anterior do JUnit, existiam 2 métodos que eram utilizados para
configurar o ambiente de teste antes e depois da execução de cada caso. O JUnit
4 substituiu os métodos por duas anotações @Before @After as duas
respectivamente substituem os métodos setUp() e tearDown(). Qual a diferença??
A diferença é que agora ganhamos uma flexibilidade maior para definirmos os
nomes dos métodos de inicialização da unidade de teste visto que o framework
agora faz interpretações a partir das anotações.
Abaixo segue um exemplo comparativo de um
método setUp() feito na versão anterior do JUnit (Listagem 4) e usando anotação
na versão 4 do framework (Listagem 5).
protected void setUp() { System.setErr(new PrintStream(new ByteArrayOutputStream())); inputDir = new File("data"); inputDir = new File(inputDir, "xslt"); inputDir = new File(inputDir, "input");
} Listagem 4 – Exemplo de código JUnit 3 ou anterior
utilizando setUp()
@Before protected void inicializa() { System.setErr(new PrintStream(new ByteArrayOutputStream())); inputDir = new File("data"); inputDir = new File(inputDir, "xslt"); inputDir = new File(inputDir, "input");
} Listagem 5 – Exemplo de código JUnit 4 usando anotação
Uma grande observação a ser feita e é destaque nessa
nova versão do JUnit são os nomes dados as anotações o que torna o processo de
elaboração das classes de teste mais intuitiva e clean.
Note que utilizando anotações @Before, você
pode criar múltiplos métodos de teste que serão executados antes de cada método
de teste definido na classe.
O antigo método tearDown() segue a mesma regra podendo
ser utilizado em múltiplos métodos para finalização de teste.
protected void tearDown() { doc = null; System.gc();
} Listagem 6 – Exemplo de código JUnit 3 ou anterior
utilizando tearDown()
@After protected void finalizaDocumento() { doc = null; System.gc();
} Listagem 7 – Exemplo de código JUnit 4 usando anotação
Para classes de teste que possuam superclasses não é
mais necessário explicitar as chamadas de métodos setUp() e tearDown() nas
superclasses. O framework se encarrega de chamar os métodos para você de forma
automática se necessário. Métodos com anotações @Before na superclasse são
chamados antes dos métodos @Before na subclasse. Métodos @After são chamados de
forma inversa, primeiro os da subclasse e depois os da superclasse.
Suíte-wide initialization O JUnit 4 acrescentou uma nova funcionalidade que
executada instruções antes da classe de teste e logo após a sua execução:
@BeforeClass e @AfterClass.
private PrintStream systemErr; @BeforeClass protected void redirectStderr() { systemErr = System.err; // Hold on to the original value System.setErr(new PrintStream(new ByteArrayOutputStream())); } @AfterClass protected void tearDown() { // restore the original value System.setErr(systemErr);
} Listagem 8 – Exemplo de anotações suite-wide.
Imagine que você precisa salvar informações numa tabela
do seu banco de dados em tempo de execução das suas classes de teste. A
abertura e fechamento de conexões é um processo custoso e que ficaria inviável
estar re-criando essa estrutura de dados a cada método de teste. Logo com as
anotações @BeforeClass e @AfterClass é possível realizar os testes e garantir
que as instruções de conexão e desconexão com o banco de dados seja realizada
apenas uma vez.
Exceções no teste Teste de exceções foi uma das maiores melhoras
implementadas pelo JUnit 4. O antigo estilo de testar as exceções exigia que
fosse implementado um bloco try/catch em volta do método que lança a exceção e
dentro do bloco catch um método para validar a exceção.
public void testDivisaoPorZero() { try { int n = 2 / 0; fail("Divisão por zero!"); } catch (ArithmeticException success) { assertNotNull(success.getMessage()); }
} Listagem 9 – Exemplo de anotações suite-wide.
Era necessário saber no código de teste qual seria a
instrução que iria levantar a exceção a ser testada e logo após colocar uma chamada
para o método fail() que indica que o teste falhou.
O JUnit 4 trouxe de uma forma simples e elegante uma
anotação na qual é informada a exceção esperada em um dado método de teste e se
a exceção não for levantada ou ainda que levantada não seja a esperada o teste
é tido como falho.
@Test(expected=ArithmeticException.class) public void divideByZero() { int n = 2 / 0;
} Listagem 10 – Exemplo de anotações exception expected.
Caso no teste seja necessário validar se a mensagem de
erro está coerente com o teste é possível incluir o bloco try/catch e realizar
a verificação desse tipo de rotina.
Ignorando testes Quando se é necessário desativar ou ignorar na classe
de teste algum método utilizamos a anotação @Ignore para substituir a anotação
@Test assim não é preciso excluir ou comentar o código dos testes ignorados.
@Test(expected=ArithmeticException.class) public void divideByZero() { int n = 2 / 0;
} Listagem 11 – Ignorando um teste.
Testes temporizados Com o JUnit 4 é possível inserir parâmetros de timeout
para o teste e definir um tempo tolerável de execução do mesmo, caso o tempo
estoure o teste é considerado falho.
Esta anotação auxilia testes que contenham dentro de
sua estrutura instruções bloqueantes como comandos de socket e acesso a banco
de dados, por exemplo.
@Test(timeout=500) public void selectAllElements() { doc.query("select * from elements");
} Listagem 12 –
Anotação para testes temporizados.
Novas assertions No framework JUnit 4 foram adicionadas 2 tipos
diferente de métodos assert() para a comparação de arrays.
public static void assertEquals(Object[] expected, Object[] actual) public static void assertEquals(String message, Object[] expected,
Object[] actual) Listagem 13 – Acerts
de comparação de arrays.
Considerações Finais A versão 4 do JUnit não pode ser considerada uma
versão de atualização do anterior e sim um novo framework. Embora simples e
dinâmico o JUnit 4 deixa a desejar em alguns aspectos os quais são
implementados pela versão anterior do Framework como a distinção entre uma
falha (erros antecipados verificado por métodos assert()) e erros (erros não
antecipados indicados por exceções). Outro ponto negativo do novo framework é a
retirada do método suíte() que permitia a execução de múltiplas classes de
teste.
No próximo artigo irei apresentar de forma prática
como instalar e executar classes de testes no Eclipse IDE e irei comentar
alguns exemplos de padrões de classe de teste.
Bons estudos e até a próxima...
|