quinta-feira, 19 de junho de 2008

Testes, testes e mais testes

Leva algum tempo para um programador entender o que realmente significa "Teste". É comum encontrar pessoas em níveis mais elevados da carreira de desenvolvedor e que nunca tiveram contato com qualquer tipo de automatização de testes.

De um modo geral o que percebo é que na maioria das vezes as responsabilidades de desenvolvimento e testes são segregadas e delegadas a duas ou mais equipes completamente distintas que executam seus supostos papéis sem muito comprometimento com o resultado do projeto em si.

Pois bem, na equipe em que trabalho há uma preocupação muito forte no que diz respeito à qualidade de software. Lutamos muito para conseguir manter padrões discutidos e acordados com os membros da equipe. Muitos de nós já somos "Test infected" e tenho notado que quando o desenvolvedor se preocupa efetivamente com os testes também ganhamos no que diz respeito a qualidade da arquitetura dos componentes que estão em desenvolvimento. Isto me faz pensar sobre o ambiente em que atuo...

Nosso cenário envolve uma série de sistemas legados que temos de evoluir e dar suporte à operação, porém há alguns projetos novos em andamento. Nos projetos novos tem sido possível desfrutar dos melhores frameworks, técnicas e boas práticas. Contudo um grande desafio tem sido evoluir sistemas que já suportam grandes operações, visando trazer inovação tecnologica, conceitos e boas práticas em códigos e arquiteturas que não foram originalmente pensadas para possibilitar a implementação de testes, por exemplo. Em grande parte do tempo é necessário lidar com refatoramentos para remover o tão conhecido "C way of doing things" que foi implementado em Java em épocas passadas.

Mas como transformar sistemas complexos (JEE) que tem interações com múltiplos clientes (sim você já distribuiu pacotes JAR contendo as interfaces para os EJBs e objetos de transferência que seus clientes devem conhecer) ?

É muito importante manter algumas coisas em mente, a primeira delas é: "O contrato que já foi definido com os clientes é (quase) sagrado."

Não é boa idéia pensar em mudar interfaces remotas de SessionBeans, ou então remover atributos de VOs sem comunicar seus clientes, os efeitos podem ser altamente indesejáveis.

Lembro-que que quando comecei a programar em Java não compreendia muito bem qual era o papel do serialVersionUID em classes que implementam java.io.Serializable. A coisa era mais ou menos assim: "Se a VM calcula o número, porque raios tenho eu que definí-lo ?"

Pois bem, não demorou muito e eu percebi o motivo disto na prática. Um dos sistemas que temos baseado em JEE sofreu uma alteração. A alteração foi feita para adicionar um atributo a mais num objeto recebido como parâmetro na chamada de um método. Apenas para para situar no tempo, isto foi por volta do fim de 2003. Ok, o objeto foi alterado, o deploy feito no ambiente de staging, e tudo foi bem nos testes com o sistema que demandava esta alteração.

Mas quando tudo mais parecia ok desenvolvedores de outras equipes nos procuravam para entender uma Exception que acontecia em seus programas ao chamar o método. O erro era relacionado ao processo de serialização porque a versão da classe no cliente era diferente da que estava no servidor.

Após algum tempo de pesquisa percebi que, de fato, o serialVersionUID tinha uma função e que este erro poderia ter sido evitado caso o valor estivesse definido na classe ao invés de ser calculado a cada vez pela VM.

Se pesquisar um pouco vai descobrir que você pode adicionar novos atributos sem problemas em uma classe sem precisar distribuir novos pacotes client para seus clientes, desde que você mantenha o serialVersionUID da sua classe. O cliente que não tiver o atributo adicional apenas não irá carregá-lo, mas o sistema continuará funcionando se ele não depender efetivamente deste novo atributo (uma informação opcional, por exemplo).

A partir deste momento, passamos a definir o serialVersionUID em todas as classes que implementam java.io.Serializable e para as que já exitiam calculamos o valor atual para definí-lo na classe e evitar problemas futuros. De fato isto nos ajudou muito em diversas situações desde então.

Vou procurar algum exemplo de código para isto e postar aqui novamente para ficar mais claro. De qualquer forma, lembre-se: implementou Serializable, não se esqueça de definir o serialVersionUID.

A propósito, ler "Effective Java" do Bloch é muito bom para qualquer desenvolvedor java, a segunda edição já saiu e aborda os recursos implantados a partir da versão 1.5. Vale muito a pena investir algum tempo lendo-o!

Abraços.
Leandro

Nenhum comentário: