Técnico

Testes Unitários com JUnit – De volta ao básico

Já que ultimamente estamos falando bastante de testes unitários, principalmente aqui na redspark, vamos revisar uma das ferramentas essenciais para executar essa tarefa: JUnit. Mais especificamente, vamos fazer alguns testes com o JUnit 4.8.1, que pode ser encontrado para download no site do projeto, ou até mesmo no repositório do maven.
A proposta desse post é apresentar a ferramenta para quem ainda não conhece, e relembrar ou até mesmo mostrar algumas funcionalidades muito úteis para nosso dia a dia de desenvolvimento.

Um pouco sobre a nova versão

Nas versões anteriores do JUnit, da 3.* pra baixo para ser mais exato, era necessário criar as classes de testes seguindo uma hierarquia pré-definida do JUnit para que os testes fossem executados. Era necessário extender uma das classes de Test Case do JUnit, e seus métodos precisavam seguir um padrão de nome especí­fico definido pelo framework.
Com a versão 4.* e a introdução ao suporte a Java 5, agora todas as configurações de testes unitários em JUnit são feitas via anotações, o que na minha opinião é muito mais rápido e fácil, tornando muito mais agradável e flexí­vel escrever testes unitários. Agora é possí­vel definir umahierarquia especí­fica para os testes do projeto, podendo abstrair muitas inicializações e padrões do sistema, facilitando o reaproveitamento e aumentando a velocidade de desenvolvimento. Afinal a maior parte do tempo gasto em desenvolvimento é com os testes.
Porém, com anotações, perdemos o acesso direto aos métodos de asserção de valores que as super classes definiam. A solução adotada foi tornar todos esses métodos estáticos e públicos, em uma classe especí­fica para guardá-los: org.junit.Assert.
Pode parecer uma solução não muito elegante do ponto de vista de código, e de fato não é quando consideramos código que será distribuí­do e deploiado, porém é uma solução que faz total sentido no escopo dos testes unitários, tornando fácil o uso e acesso a tais funcionalidades.

Asserções

Para testar nosso código, o JUnit fornece os métodos de assert. O conceito é muito simples, todo método de asserção recebe um valor que é o correto esperado pelo teste, e o outro valor que é o devolvido pelo seu código. A comparação é executada, e o teste falha caso sejam diferentes e passa caso sejam iguais. Apenas com esse conceito é possí­vel testar todo o código, basta saber quais são os valores que devem ser testados para garantir o funcionamento do código.
A chave para escrever um teste unitário que cobre muito bem o seu código, é colocar as asserções nos valores realmente relevantes ao funcionamento do sistema. Algumas vezes por exemplo, não é preciso testar um valor intermediário gerado pelo código, apenas o resultado final, outras vezes esse valor intermediário gerado é crucial para o resultado final, e portanto deve ser verificado também.
Quando eu menciono ‘valores’, entenda que um valor pode ser qualquer objeto Java, portanto é muito importante implementar o equals e hashcode de seus objetos de resposta que serão testados pelo JUnit.

Exemplo Prático

Vamos criar um exemplo de classe de testes com o JUnit 4 para vermos como funciona na prática a execução de testes unitários.
Se você utiliza o Eclipse, você já possui instalado o plugin de execução de testes do JUnit, caso você não tenha tal plugin, recomendo que instale posi facilita muito a execução e depuração dos testes.
Vamos criar uma classe de testes:


[cc lang=”java” width=”100%”]
public class JUnitTestCase {

}
[/cc]


Repare que apesar do nome parecer que segue algum padrão, não é necessário que a classe tenha nenhuma dessas palavras em seu nome. Porém esta classe ainda não é uma classe de testes do JUnit. Para torná-la um teste, crie um método da seguinte forma:


[cc lang=”java” width=”100%”]
@Test
public void metodoQualquer() {

}
[/cc]


Repare na anotação org.junit.Test. Essa anotação diz que nosso método ‘metodoQualquer’ é um teste do JUnit. Perceba também que seu retorno é ‘void’ e ele não recebe nenhum argumento. Agora nossa classe é um teste propriamente dito. Simples assim. Vamos adicionar uma asserção agora para ver o funcionamento da mesma. Dentro do método que acabamos de criar, adicione a seguinte chamada:


[cc lang=”java” width=”100%”]
Assert.assertEquals(“2 dividido por 2 deveria ser 1.”, 1, 2 / 2);
[/cc]


Repare que o primeiro argumento do método, é a mensagem que vai aparecer caso o método falhe. Mude o valor obtido (último argumento) para ver a mensagem de erro.
Esse é o básico de execução de testes. Por mais simples que possa parecer, esse é o ponto de partida. Agora existem outras funcionalidades qua ajudam a escrever testes mais complexos, por exemplo, se precisarmos criar um objeto mais complexo para nossos testes, fazemos o seguinte, adicione o seguinte código em nossa classe de testes:


[cc lang=”java” width=”100%”]
private String stringLocal;

@Before
public void inicializaLocal() {
System.out.println(“Inicializou.”);
this.stringLocal = “inicializada”;
}

@Test
public void testInicializadaLocal1() {
Assert.assertEquals(“A String deveria estar inicializada.”, “inicializada”,
this.stringLocal);
}

@Test
public void testInicializadaLocal2() {
Assert.assertEquals(“A String deveria estar inicializada.”, “inicializada”,
this.stringLocal);
}

@After
public void setaNull() {
System.out.println(“Setou nulo.”);
this.stringLocal = null;
}
[/cc]


Rode o teste e veja o que aparece no console.
Repare que a String é inicializada e setada novamente pra ‘null’ 3 vezes. Isso porque nossa classe possui 3 métodos de testes, e os métodosanotados com @Before rodam sempre antes de todos os métodos de teste. O mesmo vale para os métodos anotados com @After, só que estes rodam depois de executar os métodos de teste.
Só com essas duas anotações é possí­vel criar cenários que estão sempre ‘zerados’ e corretamente incializados para cada teste que será executado em sua classe. Perceba que com isso é possí­vel separa melhor as asserções em suas classes em mais métodos, deixando mais especí­fico e focado cada método de teste.
Porém algumas vezes queremos inicializar algum objeto para o teste todo, sem precisar de algo especí­fico para cada execução. Nesse caso existem duas outras anotações que podem ser úteis. Adicione o seguinte trecho em nossa classe de testes:


[cc lang=”java” width=”100%”]
@BeforeClass
public static void antesDeTudo() {
System.out.println(“Executado Antes.”);
}

@AfterClass
public static void depoisDeTudo() {
System.out.println(“Executado Depois.”);
}
[/cc]


Só existe um restrição com essa abordagem, e acho que está claro no código qual que é: o escopo dessas chamadas é estático. Repare que os métodos precisam ser estáticos, e portanto as incializações só servirão para propriedades que são estáticas em sua classe de testes.
Essa funcionalidade possui esse comportamento porque o JUnit instancia um novo objeto da sua classe de testes para cada método que será rodado, dessa forma ele garante um melhor isolamento dos testes, tornando-os mais unitários por assim dizer. Dessa forma somente métodos estáticos são garantia de execução antes de todos os outros métodos.
Rode o teste e veja a ordem das mensagens em seu console.

Próximos passos

Essa foi uma introdução muito simples do JUnit e testes unitários. Acho que já passou pela sua cabeça muitas formas de inicializar, integrar e rodar testes em sua aplicação usando JUnit, o que é ótimo, mas ainda existem boas práticas para criar testes assim como existem boas práticas para escrever código, afinal testes são linhas de código também.
O segredo de um bom teste unitário é o quanto ele consegue cobrir do funcionamento do código, sem que seja necessário escrever um teste extremamente detalhado que deixe o código acoplado demais, e não permita muita mudança no código original. Se você investir tempo demais testando TODOS os valores possí­veis de suas classes de maneira extremamente detalhada, quando o cliente pedir que um requisito mude, você com certeza vai ter a sensação de trabalho jogado fora, e desânimo por ter que escrever tudo novamente. A idéia é utilizar padrões de design para testes unitários, de forma que se mantenha a cobertura de código no 85%+ e ainda deixe os testes bem flexí­veis a mudança. Difí­cil mas não impossí­vel, e sim, é muito mais difí­cil e trabalhosos escrever testes realmente bons, do que escrever o código que será testado.

Por @Gust4v0_H4xx0r

2 Comments

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Compartilhe isso: