Clean Code (Código limpo) – Testes Unitários

Hoje em dia, se dependesse da minha escolha, eu escreveria um teste unitário que garantisse que cada canto do código funcionasse como esperado. Criando um projeto de testes unitário que testa cada opção de erro e garantia a integridade do código.

Os movimentos Ágil e TDD estimularam muitos programadores a escrever testes unitários, e muitos outros estão se juntando a eles todos os dias. Mas na corrida louca para adicionar testes ao nosso código, muitos programadores perderam alguns dos pontos mais sutis e importantes, devemos sempre escrever bons testes.

As Três Leis do TDD

Acredito que todos sabem que o TDD nos pede para escrever os testes primeiro, antes de escrever o código de produção. Mas essa regra é apenas a ponta do iceberg. Vamos considerar as três leis a seguir:

Primeira Lei – Você não pode escrever código de produção antes de escrever um teste unitário reprovado.

Segunda Lei – Você não pode escrever mais de um teste unitário do que for suficiente para falhar, e não compilar é falhar.

Terceira Lei – Você não pode escrever mais código de produção do que o suficiente para passar no teste atualmente reprovado.

Essas três leis prendem você em um ciclo de talvez alguns minutos. Os testes e o código de produção são escritos juntos, com os testes apenas alguns minutos antes do código de produção.

A grande maioria desses testes, que podem atingir o tamanho do próprio código de produção, pode apresentar um problema assustador de gerenciamento.

Manter os testes limpos

Quanto mais sujos os testes, mais difíceis são de mudar, e o problema é que os testes devem mudar à medida que o código de produção evolui. Quanto mais emaranhado o código de teste, mais provável é que você gaste mais tempo criando novos testes do que para escrever o novo código de produção.

Conforme você modifica o código de produção, os testes antigos começam a falhar e a confusão no código de teste torna difícil fazer com que esses testes passem. Portanto, os testes passam a ser vistos como uma responsabilidade cada vez maior.

De release em release, o custo de manutenção dos testes da equipe aumentou. Eventualmente, tornou-se a maior reclamação entre os desenvolvedores. Quando os gerentes perguntaram por que as estimativas estão ficando tão grandes, os desenvolvedores culpam os testes. No final, são forçados a descartar totalmente a suíte de testes.

Sem um conjunto de testes, eles perdem a capacidade de garantir que as alterações em seu código funcionem conforme o esperado. Sem um conjunto de testes, eles não podem garantir que as alterações em uma parte do sistema não danificam outras partes do mesmo. Portanto, sua taxa de defeitos começa a aumentar. À medida que o número de defeitos indesejados aumenta, eles começam a temer as mudanças.

A moral da história é simples: o código de teste é tão importante quanto o código de produção. Não é um cidadão de segunda classe. Requer pensamento, design e cuidado. Deve ser mantido tão limpo quanto o código de produção.

Os testes habilitam as -ilities

-ilities vem do inglês, e representa o final o final das palavras: Usability, Maintainability, Scalability, Availability, Extensibility, Portability etc.

São os testes unitário que mantêm nosso código flexível, sustentável e reutilizável. A razão é simples. Se você tem testes, não tem medo de fazer alterações no código! Sem testes, toda mudança é um possível bug. Não importa o quão flexível seja sua arquitetura, não importa o quão bem particionado seu design, sem testes você ficará relutante em fazer alterações por causa do medo de introduzir bugs não detectados.

Portanto, ter um conjunto automatizado de testes unitários que cubram o código de produção é a chave para manter seu design e arquitetura o mais limpo possível. Os testes habilitam todas as -ilities, porque os testes permitem a mudança.

Testes Limpos

O que torna um teste limpo? A legibilidade talvez seja ainda mais importante em testes unitário do que no código de produção. O que torna os testes legíveis? A mesma coisa que torna todo o código legível: clareza, simplicidade e densidade de expressão. Em um teste, você quer dizer muito com o mínimo de expressões possível.

Os testes devem seguir a convenção de código limpo para mostrar tudo de uma forma muito mais limpa e explicativa.

Uma boa prática é sempre seguir os 3As (Arrange, Act, Assert)

Um padrão duplo

Existem coisas que você talvez nunca faça em um ambiente de produção que são perfeitamente adequadas em um ambiente de teste. Essa é a natureza do padrão duplo. Normalmente, estas coisas envolvem problemas de memória ou eficiência da CPU. Mas nunca devem envolver questões de limpeza de código.

Uma afirmação por teste

Há uma escola de pensamento que diz que toda função de teste deve ter uma e apenas uma declaração ‘assert‘. Essa regra pode parecer draconiana, mas a vantagem pode ser vista claramente quando é utilizada. A leitura de testes com essa abordagem chegam a uma única conclusão que é rápida e fácil de entender.

Se você acha que fazer isso vai gerar um monte de código duplicado, podemos eliminar a duplicação usando o padrão TEMPLATE METHOD por exemplo. Ou poderíamos criar uma classe de teste separada para testar outras propriedades.

Eu não tenho medo de colocar mais de uma afirmação em um teste. Acho que a melhor coisa que podemos dizer é que o número de afirmações em um teste deve ser minimizado.

Conceito Único por Teste

Talvez uma regra melhor seja testar um único conceito em cada função de teste. Não queremos funções de teste longas que testam coisas diversas, uma após a outra.

Portanto, não são as múltiplas afirmações em cada seção que causam o problema. Em vez disso, é o fato de haver mais de um conceito sendo testado. Portanto, provavelmente a melhor regra é que você deve minimizar o número de afirmações por conceito e testar apenas um conceito por função de teste.

F.I.R.S.T.

Os testes de limpeza seguem cinco outras regras que formam a sigla acima:

Fast: Os testes rápidos devem ser rápidos. Eles devem ser executados rapidamente. Quando os testes são lentos, você não vai querer executá-los com frequência. Se você não executá-los com frequência, não encontrará problemas com antecedência suficiente para corrigi-los facilmente. Você não se sentirá tão livre para limpar o código. Eventualmente, o código começará a apodrecer.

Independent: Os testes não devem depender uns dos outros. Um teste não deve estabelecer as condições para o próximo teste. Você deve ser capaz de executar cada teste independentemente e na ordem que desejar. Quando os testes dependem uns dos outros, o primeiro a falhar causa uma cascata de falhas, dificultando o diagnóstico e ocultando defeitos.

Repeatable: Os testes devem ser repetidos em qualquer ambiente. Você deve ser capaz de executar os testes no ambiente de produção, no ambiente de controle de qualidade e em seu laptop enquanto dirige para casa no trem sem uma rede. Se seus testes não puderem ser repetidos em qualquer ambiente, você sempre terá uma desculpa para explicar por que eles falham. Você também não conseguirá executar os testes quando o ambiente não estiver disponível.

Self-Validating: Os testes devem ter uma saída booleana. Eles passam ou falham. Você não deve ter que ler um arquivo de log para saber se os testes foram aprovados. Você não deve ter que comparar manualmente dois arquivos de texto diferentes para ver se os testes passam. Se os testes não forem autovalidantes, a falha pode se tornar subjetiva e a execução dos testes pode exigir uma longa avaliação manual.

Timely: Os testes precisam ser escritos em tempo hábil. Os testes unitários devem ser escritos antes do código de produção que os faz passar. Se você escrever testes após o código de produção, poderá achar o código de produção difícil de testar. E ele provavelmente precisará de ser refatorado antes de passar em todos os testes.

Conclusão

Mal arranhamos a superfície deste tópico. Na verdade, acho que um livro inteiro poderia ser escrito sobre testes limpos. Os testes são tão importantes para a saúde de um projeto quanto o código de produção. Talvez eles sejam ainda mais importantes, porque os testes preservam e aumentam a flexibilidade, manutenção e reutilização do código de produção.

Portanto, mantenha seus testes constantemente limpos. Trabalhe para torná-los expressivos e sucintos. Se você deixar os testes apodrecerem, seu código também apodrecerá.

Qualquer dúvida ou dicas, entre em contato: leandrolt@gmail.com

Referência

– Clean Code: A Handbook of Agile Software Craftsmanship (English Edition) – Robert C. Martin – Capítulo 09

Leave a Reply

Your email address will not be published. Required fields are marked *