Desenvolvimento

18 mai, 2017

Testes de primeira classe

Publicidade

Eu acredito que possa ser o meu destino encontrar textos escritos por pessoas que ficaram presas em disciplinas infelizes que as levaram a desistir de testes unitários. Este artigo é apenas mais um deles.

O autor conta sobre como seus testes unitários são frágeis porque ele mocka todos os colaboradores (suspiro). Cada vez que um colaborador muda, os mocks têm que ser mudados. E isso, naturalmente, torna os testes unitários frágeis.

O autor continua nos dizendo como ele abandonou o teste unitário e começou a fazer o que ele chamou de “System Testing”/“Teste do Sistema” no lugar. Em seu vocabulário, “Testes de Sistema” eram simplesmente testes que são mais ponta-a-ponta do que “testes unitários”.

Então, primeiramente, algumas definições. Perdoe-me por minha arrogância, mas existem tantas definições diferentes de “teste unitário”, “teste de sistema” e “teste de aceitação” por aí, que me parece que alguém deveria fornecer uma única definição oficial. Eu não sei se essas definições vão ficar, mas espero que algum conjunto de definições permaneça num futuro próximo.

  • Teste Unitário: Um teste escrito por um programador com a finalidade de garantir que o código de produção faça o que o programador espera que ele faça (por agora, vamos ignorar a noção de que os testes unitários também auxiliam o projeto etc.).
  • Teste de Aceitação: Um teste escrito pela empresa com a finalidade de garantir que o código de produção faça o que a empresa espera que ele faça. Os autores desses testes são pessoas de negócios ou pessoal técnico que representa o negócio, por exemplo, Analistas de Negócios e QA.
  • Teste de Integração: Um teste escrito por arquitetos e/ou líderes técnicos com a finalidade de assegurar que um subconjunto de componentes do sistema opera corretamente. Esses são testes plumbing. Eles não são testes de regras de negócios. Seu objetivo é confirmar se o subconjunto foi integrado e configurado corretamente.
  • Teste de Sistema: Um teste de integração escrito com a finalidade de assegurar que os internals de todo o sistema integrado funcionem como planejado. Estes são testes de encanamento. Eles não são testes de regras de negócios. Seu objetivo é confirmar se o sistema foi integrado e configurado corretamente.
  • Microteste: Um termo cunhado por Mike Hill (@GeePawHill). Um teste unitário escrito em um escopo muito pequeno. O objetivo é testar uma única função ou um pequeno agrupamento de funções.
  • Teste Funcional: Um teste unitário escrito em um escopo maior, com mocks apropriados para componentes lentos.

Dadas essas definições, o autor do texto acima desistiu de microtestes mal escritos em favor de testes funcionais mal escritos. Por que mal escritos? Porque, em ambos os casos, o autor descreve esses testes como acoplados a coisas a que não deveriam estar acopladas. No caso de seus microtestes, ele estava usando muitos mocks, e profundamente acoplando seus testes à implementação do código de produção. Isso, naturalmente, leva a testes frágeis.

No caso de seus testes funcionais, o autor os descreveu como indo diretamente da UI para o banco de dados, e fez referência ao fato de que eles eram lentos. Ele aplaudiu a noção de que seus testes às vezes poderiam ser executados em tão pouco tempo quanto 15 minutos. 15 minutos é muito tempo para esperar pelo tipo de feedback rápido que os testes unitários devem nos dar. TDDs não têm o hábito de esperar que o sistema de construção contínua descubra se os testes passam.

Os TDDs especializados entendem que nem os microtestes, nem os testes funcionais (nem mesmo os testes de aceitação) devem ser acoplados à implementação do sistema. Eles devem (como o autor do texto nos encoraja) ser considerados como parte do sistema e “…tratados como um cidadãos de primeira classe: [Eles] devem ser tratados da mesma forma que se trataria o código de produção”.

O que o autor do artigo parece não reconhecer é que os cidadãos de primeira classe do sistema não devem ser acoplados. Alguém que trata seus testes como cidadãos de primeira classe vai ter grandes dores para garantir que esses testes não estejam fortemente acoplados ao código de produção.

O desacoplamento de microtestes e testes funcionais do código de produção não é particularmente difícil. Requer, de fato, alguma habilidade em design de software e algum conhecimento das técnicas de dissociação. Geralmente, uma boa dose de design OO e inversão de dependência, juntamente com o uso criterioso de alguns Design Patterns (como Facade e Strategy) são suficientes para desacoplar até mesmo o mais prejudicial dos testes.

Infelizmente, muitos programadores pensam que as regras para os testes unitários são diferentes – que eles podem ser “código lixo” escrito fazendo uso de qualquer estilo ad hoc que eles acharem conveniente. Além disso, muitos programadores leram os livros sobre mocking e aceitaram a noção de que as ferramentas de mocking são uma parte intrínseca e necessária do teste unitário. Nada poderia estar mais longe da verdade do que isso.

Eu, por exemplo, raramente uso uma ferramenta de mock. Quando eu preciso de um mock (ou, melhor, de um Test Double) eu mesmo o escrevo. Não é muito difícil escrever test double. Meu IDE me ajuda muito com isso. Além do mais, escrever o Test Double eu mesmo me encoraja a não escrever testes com Test Double, a menos que seja realmente necessário. Em vez de usar o Test Double, eu me afasto um pouco de microtestes e escrevo testes que estão um pouco mais próximos de testes funcionais. Isso também me ajuda a desacoplar os testes dos internos do código de produção.

Tudo se resume a quando as pessoas desistem de testes unitários. Geralmente é porque elas não seguiram o conselho do autor acima. Elas não trataram os testes como cidadãos de primeira classe. Eles não trataram os testes como se fossem parte do sistema. Não mantiveram esses testes com os mesmos padrões que aplicam ao restante do sistema. Em vez disso, elas permitiram que os testes pudessem apodrecer, se tornassem acoplados, se tornassem rígidos, frágeis e lentos. E, então, em frustração, elas desistem dos testes.

Moral: Mantenham seus testes limpos. Trate-os como cidadãos de primeira classe do sistema.

***

Uncle Bob faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://blog.cleancoder.com/uncle-bob/2017/05/05/TestDefinitions.html.