Tag Archives: testes

Um pequeno estudo sobre asserções em testes

Muitas pessoas já ouviram falar da regra “apenas uma asserção por teste” (only one assertion per test), famosa no post do Dave Astels. A regra, como o próprio nome diz, afirma que o programador nunca deve escrever mais de uma asserção pois a necessidade de mais de uma asserção em um teste poderia indicar que o método está fazendo coisas demais.

Há um mês atrás comecei a escrever um post sobre isso, mas meu exemplo era muito fraco. Corri então para olhar testes de alguns dos últimos projetos que participei, e pasmém: na maioria deles só havia uma asserção!

Ao observar os testes que continham mais de uma asserção, percebi que eles aconteciam nos seguintes casos:

  1. Quando o método retorna uma lista, array ou uma classe responsável por armazenar uma coleção de determinado objeto;
  2. Quando o método retorna um novo objeto;
  3. Quando o método é parte de uma DSL;

Nos testes do tipo (1), as asserções mais comuns são para verificar o tamanho da lista e se o elemento existente nela é o esperado. Além disso, se o teste espera mais de um elemento na lista, o teste faz algum tipo de loop para verificar; Nos testes do tipo (2), o método de teste realiza asserções sobre os atributos do objeto retornado; E, finalmente, nos testes do tipo (3), que geralmente testam a DSL, e nesse caso um teste realmente testa mais de um comportamento ao mesmo tempo, e por isso, verifica mais de um comportamento.

Resolvi portanto perguntar a opinião de outros desenvolvedores sobre a regra. Para isso, enviei a pergunta no twitter (de forma não enviesada, perguntando apenas a opinião e, para os que me responderam de volta, exemplos de código aonde isso fizesse sentido).

Algumas pessoas concordaram com a ideia de apenas uma asserção por teste, afirmando que a regra ajuda a manter o código mais simples e mais fácil de manter. Além disso, quando o teste quebra, é fácil perceber o problema.

Outros já discordaram da regra, e afirmaram que o programador deve tentar escrever sempre o menor número possível de asserções, mas que a regra não precisa ser seguida à risca. Discutindo um pouco melhor esse ponto de vista, muitos deles afirmaram que um teste deve testar apenas uma única funcionalidade, não importando o número de asserções necessárias para tal.

Infelizmente a quantidade de códigos enviada foi muito baixa. Mas, o interessante é que um dos códigos enviados se encaixa em (1). Já o outro código enviado, retirado de um livro de Ruby, não se encaixa em nenhum dos exemplos acima, mas pode-se dizer que aquele teste está na verdade testando duas funcionalidades: string com espaços no começo e no fim e string sem espaços.

A ideia desse post é portanto, fomentar essa discussão. Alguém tem um outro exemplo aonde mais de uma asserção por teste faça sentido, mas que não se encaixa nos casos acima?

Agradecimentos

Obrigado ao @elemarjr, @viniciusquaiato, @FabioVazquez, @pedroreys, @pisketti, @danielsilvarj, @carlosgaldino, @rodbv, @cessor, @alexandregazola, que responderam no Twitter.

Quando devo apagar testes?

O código dos testes é tão importante quanto código de produção. E provavelmente você já ouviu aquela famosa frase: “melhor do que escrever código, é apagar código!”. Quando é então que eu apago código de teste?

A primeira e mais óbvia resposta é: quando o teste deixar de fazer sentido! Se a funcionalidade foi removida, você deve atualizar sua bateria de testes e apagar todos os testes relacionados à ela. Bateria de testes desatualizada não serve pra nada! Se a funcionalidade evoluir, você deve evoluir seus testes juntos.

Até aí nada de novidade… Mas vamos lá.

A segunda resposta é: quando você tem testes repetidos! Em algumas situações, quando estamos em dúvida sobre como implementar determinada funcionalidade, optamos por escrever testes parecidos para, de alguma forma, triangularizar até chegar na implementação correta.

Voltando ao velho exemplo da calculadora. Suponha que implementar um algoritmo de soma fosse algo complicado. Você começou com testes simples, como (1+1), depois (1+2), depois (2+2). Nesse momento você encontrou uma maneira de resolver o problema para quaquer (m+n). Seus testes de unidade ficam parecidos com esses:

[gist id=674641]

Esses testes, muito úteis durante o tempo de desenvolvimento do algoritmo, agora se tornaram repetidos. Você, portanto, deve apagá-los! Eles, além de serem inúteis, ainda dificultam o trabalho do desenvolvedor. Se um dia o método testado mudar, você terá que mudar em 10, 20 testes diferentes (mas que testam a mesma coisa!). Lembre-se do acoplamento entre seu código de teste e seu código de produção (sim, ele existe!).

Mas poxa, um testezinho só não é pouco? Não! Você precisa de apenas um teste para garantir a funcionalidade. Não adianta testar a mesma coisa duas vezes.

Você deve ter apenas um teste para cada conjunto de estados válidos e inválidos para uma condição de entrada. A ideia é que todos os elementos de uma classe se comportem de maneira similar. A esses conjuntos damos o nome de classes de equivalência. Escrever apenas um teste por classe de equivalência é uma prática muito comum em testes de caixa preta e é conhecida como particionamento em classes de equivalência. Apesar disso, acredito que ela faça sentido também para testes de caixa branca, como os testes de unidade.

No nosso exemplo da calculadora, poderíamos ter testes para, por exemplo:

  • soma de dois números positivos;
  • soma de um número positivo com outro negativo;
  • soma de um número negativo com outro positivo;
  • soma de dois números negativos;
  • soma com um dos elementos sendo zero;

[gist id=674694]

Obviamente, encontrar todas as classes de equivalência não é um trabalho fácil, e por isso temos a gigante área de testes de software. Mas não é repetindo testes que você garante que seu código funciona.

Referências

Maldonado, Jino, Delamaro. Introdução ao teste de software. Editora Campus, 2007.