quarta-feira, 6 de agosto de 2008

Test Driven Development - parte 1.

Veja este código:

 public void testEhPar() throws Exception{
assertTrue("2 deve ser par",algoritmo.ehPar(2));
assertTrue("4 deve ser par",algoritmo.ehPar(4));
assertTrue("6 deve ser par",algoritmo.ehPar(6));

assertTrue("1 NAO deve ser par",!algoritmo.ehPar(1));
assertTrue("3 NAO deve ser par",!algoritmo.ehPar(3));
assertTrue("5 NAO deve ser par",!algoritmo.ehPar(5));
}


Dentro de uma classe de teste, usando o framework JUnit, a leitura dessas linhas é a seguinte:

Eu tenho um objeto chamado algortimo.
A chamada algoritmo.ehPar(2) deve retornar true, pois 2 é par, e este método informa se o parâmetro informado é, ou não, par.
O método assertTrue recebe dois parâmetros: uma mensagem informando o significado deste teste, e o resultado do mesmo.

Se o método estivesse com algum problema e retornasse false, isso:

assertTrue("2 deve ser par",algoritmo.ehPar(2));

seria o mesmo que

assertTrue("2 deve ser par",false);

Logo, o teste falha, pois 2 deve ser par.

Esta é uma forma programática de garantir o comportamento do código que eu desenvolvo: testando. Se o meu método só depende dos parâmetros informados, é muito simples verificar o seu funcionamento.

Desenvolvendo os testes antes de implementar o código, tendo apenas as assinaturas dos métodos (por isso recorri a uma interface aqui, lembra?), eu posso pensar nos comportamentos esperados e, então, vou desenvolvendo até que todos os testes estejam passando.

Não é nada fácil, funciona melhor quando o projeto que estás desenvolvendo está começando, porém os resultados são excelentes: veja o quanto de tempo estás economizando! Se tu pretendes determinar se um código teu está funcionando por programas que perguntam os valores, escrever

$ ant clean test


é muito mais rápido!

O problema surge quando queremos fazer algo complexo, pois a dificuldade de testar mostra problemas na arquitetura adotada, por exemplo. Quanto temos objetos que tem objetos, herança, polimorfismo, tecnicas mais avançadas podem ser usadas como usar Mocks de objetos (e prover os mecanismos para injetar estes Mocks).

No caso desse exemplo, esta é uma solução:
public boolean ehPar(int numero){
return numero % 2 == 0;
}


Muitos alunos desconhecem as operações de divisão e módulo entre inteiros. O 1, inteiro, dividido por 2, inteiro, não é 0.5, (em java), e sim 0, pois este é o resultado da divisão inteira. O resto da divisão, simbolizado por %, retorna 1. Dessa forma, o resto da divisão de um numero par por 2 é 0, caso contrario é um número inteiro. São operações básicas que precisam ser dominadas. Repare que eu fiz um código que funcione, não tentei nada mais maquiavélico que dê 0.0001 milissegundos mais rápido. Evite a otimização precoce.

Programar profissionalmente é uma tarefa de muita responsabilidade. Desenvolver testes para garantir a qualidade provê excelentes resultados a longo prazo.

2 comentários:

OLP disse...

"caso contrario é um número inteiro" não seria "caso contrario o numero é impar" ?

Tiago "PacMan" Peczenyj disse...

Sim sim, escrevi errado!