Unit Testing em aplicações já existentes e sem teste

Se você achou interessante começar uma aplicação com Test-Driven Development, o que dizer sobre um dos fatores mais comuns em nossa área que é assumir um projeto que já está em produção, porém não possui testes de unidade ?

Softwares legados também possuem direito de ter seu lugar ao sol !

Cenário clássico

Você entra para uma nova equipe de desenvolvimento. Como um tester acostumado com as práticas do Test-First, você vai cegamente à procura do diretório(s) com os testes para: executá-los afim de descobrir se configurou o ambiente corretamente; ler, afinal quer entender um pouco mais do projeto; depois de vasculhar /tests, /spec, /xpto/namespace/module/tests e não encontrar nada você descobre que: o projeto não possui testes!

Normal? Infelizmente sim – Há muitos desenvolvedores que não gostam de testes. Os motivos variam e é assunto futuro -, vários projetos que vi não possuiam testes at all. Em todas às vezes porém, meus gestores eram favoráveis a adoção e receptivos a mudança de comportamento que isto causaria.

Sem testes e com carta branca para começar a mudar o cenário. O que fazer agora ?

$ ./configure

Antes de tudo, você precisa ver como o projeto está feito. Passe umas horas ou dia olhando o que tem ali e por onde poderia começar a trabalhar. Após isto, vamos listar coisas importantes sobre o código que já funciona em produção:

  • Não faça testes de unidade do que já existe;
  • Não seja convencido a fazer testes de unidade do código existente;
  • É insanidade querer testar as partes de um código que já funciona. Não faça isso;
  • Não faça teste de unidade do código existente.

Motivo para todos itens acima é o mesmo: teste de unidade é seu guia para construir o software requisitado da menor e melhor forma possível. Ele é um guia e não uma Bug Tool. Se o código já está presente, você, se cair na tentação de fazer teste daquilo, forçará seu teste a passar naquele código. Seu teste pode até pegar um bug, mas você, novato no projeto, não saberá que é um bug e acabará mudando o teste para passar naquela condição bugada. Costumo dizer que testar código já feito é perder tempo.

Vou comentar dois cenários aqui: a) você precisa criar um módulo/integração/package novo dentro desse projeto legado; b) você precisa adicionar testes ao código existente.

Vamos lá 🙂

$ make all

Vou pegar os dois casos e comentários sem separado:

a) Você precisa criar um módulo/integração/package novo dentro desse projeto legado

Para mim, dentre os dois esse é o mais fácil. Tudo que você precisa fazer é começar o módulo novo de forma isolada. Ou seja: evite depender do código sem teste dentro do seu módulo novo. Você pode obter isso com o uso de Adapters. Crie seu módulo novo, seguindo Test-Driven Development e toda vez que precisar da colaboração de uma classe/objeto que não tem o devido teste, crie um Adaptador que isolará aquela dependência instável do seu código novo. Isso te dará maior confiabilidade no que está fazendo.

Isole toda a comunicação do seu módulo novo, com o uso de Façades.

Supondo que você está fazendo um módulo de pagamento, crie uma façade para seu código e a faça prover os recursos que o restante do software necessitar:

    class PaymentFacade {
        public ... create(...);
        public ... createRecurrent(...);
        public ... getPayments(Datetime forDate);
        public ... cancelRecurrent(Integer paymentId);
        public ... processPayment(Integer paymentId);
        ....
    }

Na sua Façade, trafegue valores escalares e não objetos do sistema. Isto tornará seu module de pagamento mais isolado e protegido do código não testado.

b) Você precisa adicionar testes ao código existente

Lembre-se: não adicione testes de unidade ao código já existente e sem teste. Partindo disto, seguimos:

Anticorruption Layer, DDD

Este é mais complicado. Costumo utilizar uma técnica do livro Domain-Driven Design de Eric Evans chamada Anti-corruption Layer.

A ideia aqui é fazer igual na figura: o “ACL” é o Anti Corruption Layer, a camada que irá defender seu código novo do código legado sem testes.

Dando um zoom nesta imagem, tudo que precisamos fazer é criar uma layer que conterá classes/objetos ou/e métodos em classes existentes que farão adapters/façades, traduzindo o que o sistema já tem para conseguirmos encaixar no que estamos fazendo.

DDD Zoom-in

Client System: seu sistema legado
Anticorruption Layer: o meio entre seu sistema legado e seu módulo novo
External: seu código novo, totalmente Test-First.

Não sinta-se intimidado em tentar fazer deploy de uma versão funcional com seus adapters, façades e anticorruption layer. Eu já mantive em produção por algumas semanas código “macabro”, cheio de “fios pendurados” e coisas em aberto, até que eu pudesse ajustá-las para em deploys futuros, pudesse removê-los tranquilamente.

# make install

Berlim Wall, the fall

Agora que consegue deployar seu código novo e testado junto do software que tinha antes sem testes de uma forma até que harmoniosa e isolada, você precisa derrubar o muro de Berlim que construiu: você precisará refatorar seu código legado e sem testes para que comece a adicionar testes nele também. Ao adicionar testes no código refeito, você poderá remover os Adapters, Façades e até a Anticorruption Layer daquele pedaço que isolava o código sem testes do código “novo”.

Isto é uma parte importante, pois você precisará fazer tudo de dentro para fora. Explico: fora é a parte mais próxima do seu cliente possível (Controller, API, etc); dentro é a parte mais próxima do código que está isolando/refatorando. Sempre venha de dentro para fora nas tuas refatorações, pela razão de que assim, você afetará a menor quantidade possível de classes, pois identificará logo de pronto até onde pretende refatorar, colocando logo na sequencia disto seus adapters/façades.

Concluindo

Agora você tem um bom motivo para utilizar adapters e façades. Esse tipo de refatoração é uma das minhas favoritas, pois envolve muito de lidar com isolamentos bem construídos e como manter isto de forma que não quebrará todo o software. O resultado final é uma aplicação funcional com um toque de código novo e testado ao mesmo tempo.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s