Tag Archives: testes automatizados

Maus cheiros em códigos de teste… Eles existem!

Acabei de ler um paper muito legal sobre code smells em código de testes [1]. Resumindo aqui os resultados:

  1. Maus cheiros em testes não são percebidos pelos desenvolvedores como problemas reais de código. Ou seja, desenvolvedores não costumam enxergá-los como problemas.
  2. Na maioria dos casos, as classes de teste são afetadas pelos smells desde o momento da sua criação. Ou seja, a maioria dos testes mal escritos foram mal escritos desde o começo.
  3. Os maus cheiros tem uma taxa de sobrevivência altíssima. Ou seja, o mau cheiro fica lá no código por muito tempo, até alguém decidir refatorá-lo.
  4. Existe uma relação entre maus cheiros no código de teste e maus cheiros no código de produção. Ou seja, código de produção ruim faz com que desenvolvedores escrevam testes problemáticos também.

Você quer ler mais sobre possíveis maus cheiros em código de teste? Então veja o excelente paper do Arie van Deursen et al. [2], onde eles listam diversos maus cheiros que podem acontecer nesse tipo de classe.

[1] Tufano, M., Palomba, F., Bavota, G., Di Penta, M., Oliveto, R., De Lucia, A., Poshyvanyk, D.Towards Automated Tools for Detecting Test Smells: An Empirical Investigation into the Nature of Test Smells. In 31st Automated Software Engineering, 2016. Link aberto pro paper.

[2] A. van Deursen, L. Moonen, A. Bergh, and G. Kok. Refactoring test code. In Proceedings of the 2nd International Conference on Extreme Programming and Flexible Processes in Software Engineering (XP), pages 92–95, 2001. Link aberto pro paper.

Testes e infraestrutura: inimigos de longa data

TL;DR: Suas classes com regras de negócio não podem depender de infraestrutura. Elas devem ser facilmente testáveis por meio de testes de unidade. Dependências devem ter interfaces amigáveis e serem facilmente mockadas.

Às vezes ouço perguntas como “como testo meu sistema em Android”, ou “como testo meu sistema em JSF”, ou “como testo meu sistema em <ponha-sua-tecnologia-aqui>”. Pra mim, essa pergunta faz todo sentido se você está procurando uma maneira de fazer testes de sistema. Algo como Selenium, só que Android. No entanto, se você está pensando em testes de unidade, essa pergunta não deveria ser muito importante.

Regras de negócio são importantes. Tão importantes que merecem ficar o mais afastado possível da sua infraestrutura. Ou seja, não importa se você usa JSF, Android, ou whatever, o coração do sistema deve ficar em classes Java, simples, que não dependem de nada externo. Se você pensar sempre assim, pronto, todas suas regras de negócio serão facilmente testadas.

Ah, mas e se pra realizar aquela regra de negócios, eu precisar consultar uma infraestrutura? Sei lá, pegar um dado do banco de dados, ou postar algo em um serviço web? Faça sua classe depender de uma classe que tenha uma interface bastante amigável. Algo como bd.pegaValor() ou correios.calcula(“SP”). Nada que você tenha que ficar escrevendo linhas e linhas para chegar no resultado que espera. Assim, fica fácil mockar depois para testar. E dentro da dependência, você faz o que tiver que fazer pra fazer a infraestrutura funcionar.

Se você pensar assim sempre, a infraestrutura que está usando deixa de ser importante. Afinal, você fugiu dela. E testou o que precisava ser testado.

Primeiros Passos com Testes de Unidade – Parte 2

No post passado, escrevemos testes para o FizzBuzz. E foi fácil: criamos a classe FizzBuzzTest e escrevemos lá métodos, com diferentes entradas e saídas. A grande charada é que, como conhecemos a entrada, também sabemos a saída. Mas será que os testes que temos são suficientes? Vamos agora discutir um pouco sobre o que testar.

Veja novamente nosso código de produção:

O grande truque é olhar para o código e pensar nos vários caminhos diferentes que ele pode ter. Para isso, analisarmos cada instrução que faz o fluxo do código mudar. Por exemplo, ifs, fors, whiles, etc. Em nosso FizzBuzz, temos:

  • Cenário 1: O primeiro if tem duas condições: número é múltiplo de 5 E múltiplo de 7. Se esse if der verdadeiro, o programa retorna “fizzbuzz” e acaba.
  • Cenário 2: O segundo if valida se o número é múltiplo de 5. Se der verdadeiro, o programa retorna “fizz” e acaba.
  • Cenário 3: O terceiro if valida se o número é múltiplo de 7. Se der verdadeiro, o programa retorna “buzz” e acaba.
  • Cenário 4: O quarto caminho acontece quando nenhum dos anteriores acontece. O programa retorna o número e acaba.

O próximo passo é: qual a entrada que precisamos passar para que o cenário 1 aconteça? E depois, para o cenário 2? E assim por diante. Foi o que fizemos: passamos 35 para o cenário 1 acontecer, ou 13 para o cenário 4 acontecer.

E perceba também que temos um teste para cada cenário. Preciso de mais? Geralmente não. Se você pensar bem nos cenários, verá que não precisará exercitar mais de uma entrada em cada. Chamamos essas entradas diferentes, mas que exercitam o código da mesma maneira, de classes de equivalência. Um teste por classe de equivalência é uma boa regra a seguir.

No entanto, nada te impede de ter um pouco mais de segurança. Quando lidamos com algoritmos que envolvem números, como é o caso desse, podemos exercitar cenários similares. Não só para garantir que funciona, mas também para facilitar o entendimento quando outro desenvolvedor ler o teste.

E casos excepcionais? Se você achar que seu programa precisa tratar casos excepcionais, como zero, nulo ou exceção, você então deve escrever o teste para garantir que seu programa se comportará corretamente.

Neste post, discuti sobre como deve ser seu pensamento na hora de escrever um teste. Mas ainda temos 3 posts. Estamos chegando lá! 🙂

Montando cenários em testes de sistema

TL;DR: Crie serviços web para montar os cenários que você precisa em seus testes de aceitação.

Montar cenários é sempre a parte mais chata em qualquer teste, seja ele manual, de unidade, de integração ou de sistema. O pior é que, quanto mais o teste é parecido com o do mundo real, mais difícil é. No teste de unidade, você tem uma classe, geralmente pequena, e que lida com poucas outras classes. Montar o cenário é razoavelmente fácil; você instancia duas ou três classes e pronto. Mas e quando o teste é de sistema? Como fazer para testar a tela de Nota Fiscal, se para isso, precisamos de um Cliente, Produto e Orçamento cadastrados?

A primeira opção é fazer com que o próprio teste seja responsável por navegar pelas telas anteriores e montar os cenários. Ou seja, antes de ir pra tela de nota fiscal, o teste passeia pela teste de clientes, e cadastra um por lá. Depois vai pra tela de Produto e cadastra um por lá. A mesma coisa com um Orçamento, para aí sim conseguir testar a Nota Fiscal. O problema disso é que absurdamente demorado passear por todas essas telas. E a chance de você repetir código entre as várias baterias de teste é ainda maior.

Nesse momento, meu coração diz que a melhor solução é fazer com que a aplicação Web contenha um conjunto de serviços web, prontos para montar o cenário que a aplicação quiser.

  • Precisa de um Cliente? O teste faz um post para /ws-testes/clientes, com o Cliente desejado.
  • Precisa de um Orçamento? O teste faz um post para /ws-testes/orcamentos.
  • Precisa de um Estado (e não existe tela de estado?) Não tem problema, tem o /ws-testes/estados.

Dessa forma, fica fácil. No código do teste, você faz uso de Test Data Builders para rapidamente montar esses objetos, e simplesmente os envia para a aplicação. Fazer serviços web simples, que recebem basicamente entidades, também é fácil, e seu framework MVC deve fazer isso quase que automático.

Além disso, porquê não ter também o /ws-testes/limpa ? Precisamos limpar nosso banco de dados antes de cada teste, para que um teste não influencie o outro.

Apesar de parecer pouco produtivo, na prática, os Test Data Builders usados são os mesmos que você já usa nos seus testes de unidade. E se você tem uma infraestrutura razoável, fazer serviços web é geralmente mais simples do que escrever a navegação nas funcionalidades ao redor, que você precisaria escrever de qualquer jeito.

Fuja dos XMLs malucos do DBUnit ou mesmo de testes de sistema que precisam navegar por 30 telas antes da sua. Dá trabalho? Claro que dá… Mas testar software é trabalho, não é?!