Testes automatizados frágeis e cuja manutenção seja onerosa acabam por levar as empresas a abandonar as iniciativas de automatização, de acordo com Dale Emery. Em um artigo recém-publicado, Dale compartilha algumas iniciativas práticas para se evitar problemas com automatização de testes. Ele começa com algum código típico de automatização e o evolui de forma a torná-lo mais robusto e menos custoso de se manter.
A ideia fundamental por trás do artigo de Dale é que a automatização de testes é desenvolvimento de software. É um pensamento simples, mas muito poderoso, e que ele atribui a Elisabeth Hendrickson pela sua formulação.
Para a maioria dos softwares, é provável que a manutenção custe mais que o desenvolvimento inicial, ao longo do ciclo de vida do código. No âmbito da automatização de testes, isto é aplicado pelo uso dos scripts de record-and-playback. Estes scripts são fáceis de criar, ainda que tendam a ser difíceis de manter.
Dale aponta dois dos principais fatores que devem ser considerados para se tornar os scripts de automatização de testes mais fáceis de manter: detalhes incidentais e duplicação de código.
Detalhes incidentais são tudo aquilo que é necessário para fazer o teste rodar, mas que de fato não é parte da essência do teste. Exemplos incluem: atribuição de variáveis, laços, chamadas de rotinas em baixo nível, cliques automáticos de botão e até mesmo a própria sintaxe da linguagem de script do teste. Todas essas coisas são necessárias, elas são o 'como' o código do teste deve executar, mas elas também ofuscam o 'o que' é realmente o objetivo essencial do código de teste.
Já duplicação de código é simplesmente código que aparece muitas vezes; e é um conhecido inimigo da manutenção de software. Qualquer mudança no sistema demanda que cada trecho de código duplicado precise ser corrigido. Este é um dos problemas chave com os scripts de record-and-playback de testes automatizados; eles estão repletos de códigos duplicados.
Para tornar as coisas mais palpáveis, Dave apresenta um código de exemplo que testa uma rotina de criação de contas. O código é realístico e correto, mas possui muitos detalhes incidentais e muita duplicação de código. Após sucessivas refatorações, Dale evolui o código para esconder os detalhes incidentais e remover a duplicação. O código resultante é notoriamente mais fácil de se dar manutenção. Um benefício extra obtido é que o código resultante revela com mais clareza a essência do que cada teste está tentando verificar. Mesmo sem conhecer a ferramenta de teste ou outro contexto, uma pessoa seria capaz de entender qual requisito do sistema não estará sendo cumprido quando este código falhar:
Rejects Password ${aPasswordWithNoLetters}
Voltando no tempo até 1997, durante o Los Altos Workshop on Software Testing (LAWST), um encontro de profissionais de teste de software, encontrou problemas semelhantes com respeito ao estado da automatização de testes. Cem Kaner documentou os resultados deste encontro e os apresentou na Quality Week '97. Em seu artigo Improving the Maintainability of Automated Test Suites Cem observou:
A maneira mais comum de se criar casos de teste é usar o recurso de captura de sua ferramenta de teste automatizado. Isso é um absurdo... Uma mínima alteração na interface de usuário e o script estará inválido. Os custos de manutenção relacionados com casos de testes capturados são inaceitáveis.
Seguem aqui três das sugestões do grupo do LAWST para tornar a automatização de testes mais manutenível.
Reconheça que o desenvolvimento de testes automatizados é desenvolvimento de software
De todas as pessoas, os testadores são os que mais devem compreender o quão importante é seguir uma abordagem disciplinada para desenvolvimento de software ao invés de se tentar fazer um projeto de testes rápido e rasteiro com uma implementação “nas coxas”. Se não tivermos isso, devemos nos preparar para fracassar tão miseravelmente quanto as diversas aplicações que tenhamos testado.
Utilize uma arquitetura orientada a dados
A lógica de implementação de muitos casos de testes deverá ser a mesma, mas a lógica precisa ser executada com uma variedade de entradas e com as respectivas saídas esperadas. Separando os dados da lógica do teste, a duplicação é removida. Por mais que a interface com o usuário mude, por exemplo, uma única correção sobre o código de teste correspondente pode corrigir um grande número de casos de teste.
Utilize uma arquitetura baseada em framework
O framework isola a aplicação em teste dos scripts de teste, disponibilizando um conjunto de funções em uma biblioteca compartilhada. Os criadores dos scripts de teste tratam estas funções como se fossem comandos básicos da linguagem de programação da ferramenta de teste. Eles podem ainda programar os scripts independentemente da interface de usuário do software.
Não há nada melhor do que boas práticas de programação: abstrair os detalhes de implementação confusos. As escolas de programação que pregam troca de mensagem baseada em interfaces e orientação a objetos apontam os benefícios destas práticas há anos, embora o conceito em si remonte às ideias de subrotinas.
Utilizar uma abordagem orientada a dados e implementada sobre um framework bem projetado pode reduzir drasticamente seus custos de manutenção. A questão de fato é como fazer isto. O artigo de Dale dá uma resposta: evoluir o código existente ao longo de uma série de refatorações até que ele apresente estes atributos desejáveis. Isto faz sentido quando você considera a automatização de testes como desenvolvimento de software.
E você? Quais as suas experiências com automatização de testes? Você tem tido problemas com manutenção? Que abordagens você têm tentado para superar os problemas e que resultados têm obtido? Deixe um comentário e compartilhe sua experiência com o resto da comunidade.