quinta-feira, 10 de março de 2011

Dicas de Programação: Como fazer um bom teste unitário?

Me pediram para ser um pouco menos técnico nas dicas de programação. Então decidi que vou falar de teste unitário. :)

Para quem não sabe o que é um teste unitário...
Define-se "teste unitário", em síntese, todo teste feito na menor unidade de uma aplicação (por exemplo, em funções e/ou procedimentos (código-fonte) de um programa).

Dadas as explicações, agora voltamos a pergunta: Como fazer um bom teste unitário?
Antes de fazer um teste unitário, existem algumas coisas a se pensar:

Primeiro, precisamos definir o domínio da função ou procedimento (Hã? Matemática?), ou seja, definir um conjunto de valores válidos ("entrada") para esta função.

Se definimos o domínio, agora precisamos definir o contra-domínio desta (Matemática de novo? Isso não deveria ser informática? :P ), em termos gerais, vamos definir um conjunto de valores válidos para o resultado ("saída") da função.

Para os matemáticos de plantão, isso significa que na função y = F(x), se x pertence ao domínio da função, então y pertence ao contra-domínio. Se x não pertencer ao domínio, então y não é um resultado válido.

Para facilitar as coisas (eu acho que as compliquei... :P ) vamos ao exemplo:

Vamos criar uma função que calcula a média de notas de duas provas (a primeira de peso um, a segunda de peso dois) em uma escola:

function media(primeira: integer, segunda: integer): integer
    media = (primeira + 2 * segunda) / 3;

//Teste Unitário
...
var resultado: integer;
resultado = media(5,5);
assertEquals(resultado, 5);
...

Parece que a função calcula corretamente e que o teste unitário esta passando, certo?
Errado! :P

Hã, como assim?

Vamos a alguns questionamentos que podem existir:
Existe nota negativa?
Existe nota maior que 10?
Existe nota com valor "quebrado" (5.5, por exemplo)?
Se não existe, a nota deve ser arredondada para cima ou para baixo? A partir de que valor?

Perceba que, como não foi definido um conjunto de valores válidos de entrada (domínio) e saída (contra-domínio), ficamos com um monte de dúvidas e não sabemos os valores que devemos testar...

Agora, se definirmos o domínio desta função:
As variáveis "primeira" e "segunda" pertence ao conjunto dos números inteiros, tal que, "primeira" e "segunda" são valores maiores ou iguais a 0 e menores ou iguais a 10.

E o contra-domínio desta:
O resultado da função pertence ao conjunto dos números inteiros, tal que, este é um valor maior ou igual a 0 e menor ou igual a 10, sendo resultante do seguinte calculo
(primeira + 2 * segunda) / 3, com valores arredondados para baixo, em caso de um resultado pertencente ao conjunto dos números reais.

Agora que sabemos quais os valores são válidos e não válidos para a função, podemos definir os seguintes testes:

//Valores maiores que 10 (dominio)
...
try
    resultado = media(11,21);
    fail();
except
    assertTrue(true);
...

//Valores menores que 0 (dominio)
...
try
    resultado = media(-1,-1);
    fail();
except
    assertTrue(true);
...

//Valor com um resultado quebrado (contra-dominio)
...
resultado = media(2,7);
assertEquals(resultado, 5);
...

Acho que isso torna o teste unitário mais "eficaz".

Bem, é isso...(sei lá, não sou bom de explicar essas coisas...)
Espero que isso ajude! :)

Até mais!

Nenhum comentário: