sábado, 11 de abril de 2009

TDD na Prática – Parte V: Testabilidade e a UML

Seguindo nossa pequena série sobre TDD, vamos ver sobre como a testabilidade influencia no design, sobre a diferença de escrevermos os testes antes ou depois e como a UML pode se relacionar com TDD.

(Lembrando que nas próximas partes da série será dado início à implementação de um pequeno e simples Jogo da Velha, nos moldes do TDD)

Como podemos testar um Jogo da Velha ? O que deve ser verificado ?

Esta pergunta pode gerar uma grande lista de coisas que devem ser checadas. Nela, com certeza faltarão coisas simples, óbvias, que serão deixadas de lado pelo fato de geralmente nos atermos às coisas mais importantes, de maior impacto na aplicação. Porém, devemos lembrar que antes de testarmos coisas complexas, temos que ter certeza de que as simples estão funcionando corretamente. Testando-as, teremos segurança para prosseguir para os testes mais complexos, os quais, por exemplo, envolvam a interação de diversos objetos. Neste caso, cada objeto deve ter seu comportamento individual testado, para que o novo teste se concentre na (correta) cooperação entre os mesmos.

QuebraCabeca

Assim, deve-se começar pelo trivial e ir pouco a pouco aumentando a complexidade sobre o que será verificado. Pondere, também, a importância que cada nova verificação tem para a aplicação, do ponto de vista da relação custo-benefício do teste. Veja mais sobre este assunto na Parte IV: Quem, Onde, Quando, O Que e Como.

Complexidade

Fique atento à relação Complexidade do Assunto do Teste versus Complexidade do Código do Teste. Num teste, o o que deve ser verificado pode ser complexo mas não o como. Se seu teste começar a ficar difícil de ser implementado, pare e repense o problema. Pode ser que você precise refatorar seu código de teste. Muitas vezes isto acontece devido ao código criado para o teste estar pouco coeso. Talvez porque ele esteja fazendo mais trabalho do que somente as verificações necessárias, ou talvez porque, na verdade, esteja fazendo mais que somente um teste. Analise com cuidado a responsabilidade atribuída ao método de teste criado e verifique seu é possível refatorá-lo (fazer um Extract Method, por exemplo).

Contudo, se continuar difícil testar uma determinada funcionalidade é porque não deve ser seu código de teste que está ruim, mas, infelizmente, o modelo que você criou/planejou para a(s) classe(s) participante(s) do teste não ficou “testável”.

Testabilidade do Modelo

Este conceito de “testabilidade”, ou seja, do código que você escreveu ser fácil de testar, é um dos principais aspectos do TDD. Isto porque o design de seu projeto muda para se tornar “testável”. Muitas vezes aquele diagrama de classes UML que você criou para ajudar a pensar sobre seu modelo e, sobretudo, o diagrama de seqüência que lhe ajudou a pensar na interação entre os objetos destas classes, irão sofrer alterações significativas.

Esta é a parte em que o TDD começa a guiar o design de seu modelo. Muitas vezes um modelo que parece muito bem feito num diagrama pode ser dificílimo de testar. E por que isto acontece ? Acontece porque os testes forçam seu modelo a expor responsabilidades antes não percebidas, a tornar as dependências entre as classes mais explícitas e também deixam-no mais próximo de como ele será utilizado, o que, no diagrama, pode estar bem longe da realidade (dependendo, claro, da experiência de seu criador).

Daí, os métodos mudam, outros aparecem, classes antes não detectadas surgem, dependências são deixadas visíveis, bem claras e quando você voltar e olhá-lo num diagrama para ver o que se tornou, você terá uma agradável surpresa: seu modelo melhorou !

As verificações forçarão a criação de um modelo mais simples e flexível, mais coeso e bem menos acoplado. Você perceberá quando algo no modelo estiver ruim, pois provavelmente começará a sentir dificuldades para testá-lo. Esta será a hora de adaptá-lo, de torná-lo mais leve, de deixá-lo apto a ser verificado.

Este é o impacto que diferencia criar os testes antes versus criar os testes depois. Você não tem os benefícios da melhoria do modelo se criar os testes depois. E sofrerá um bocado pra fazer alguns testes.

testability

Quando o teste é feito após o código ter sido implementado, você precisará encontrar meios de conseguir retirar a informação que você precisa verificar. Em muitos casos, como a classe não foi pensada para fornecê-la, você precisará alterar seu código ou o código de classes à ela relacionadas. Dependências não poderão ser trocadas por substitutos, pois o modelo não foi planejado para testes. Assim, tudo fica um pouco mais complicado.

Criando os testes antes, você definirá de antemão como obter as informações desejadas e planejará a possibilidade de substituição de dependências, facilitando e acelerando os testes.

UML

Então se os diagramas acabam mudando, qual a necessidade de criá-los ? Creio que a UML é uma excelente ferramenta, sendo válida independente da forma como o projeto seja implementado. Mesmo se o modelo precisar ser adaptado para ficar testável. O exercício de modelagem é importante para se criar uma visão geral do problema, pensar sobre o relacionamento das classes, antever problemas e pré-definir regras de negócio. Um diagrama de classes bem construído, por exemplo, pode servir como um bom ponto de partida para a construção do modelo que será lapidado pelos testes.

Isto pode parecer um pouco contraditório para os puristas de TDD, mas vejo a representação visual como um meio de documentação válido para o projeto. Principalmente diagramas conceituais. É muito mais simples expressar o modelo de um projeto de um forma visual e facilitar a comunicação com os integrantes da equipe. A agilidade da representação visual cai como aquela máxima: “uma imagem vale por mil palavras”.

logo_uml

São vários os benefícios da representação visual e estas justificam o uso de uma ferramenta de sincronização dos diagramas com o código, já que se tornaria inviável a atualização manual dos mesmos. Diagramas de interação, por exemplo, como o de seqüência ou de colaboração, podem sofrer tantas modificações que o custo de mantê-los pode desmotivar sua manutenção.

A contradição entre a UML e o TDD pode ser definida rapidamente assim: Com a UML, você define o modelo através da criação dos diagramas, escreve o código para implementá-los e depois cria os testes. Com TDD, você define o modelo através da criação dos testes e cria o código.

Meu ponto é: crie um rascunho para o modelo através da criação dos diagramas, deixe os testes definirem o modelo final (“testável”), crie o código e sincronize os diagramas. Assim, é tirado proveito dos dois lados.

Documentação Executável

No caso de equipes que não necessitam de diagramas como artefatos para documentação da aplicação, elas ainda podem se beneficiar da “documentação executável” criado pelos testes. Como um manual de utilização do código da aplicação, através do código dos testes é possível aprender a utilizar uma determinada classe e saber exatamente como sua instância se comporta, isoladamente ou em conjunto. Esta documentação estará sempre atualizada, pois os testes são executados freqüentemente, e conterão as regras de negócio e restrições que permeiam o modelo, sendo uma excelente forma (não visual) de conhecê-lo.

Mesmo para as equipes que usam documentação visual, a documentação criada pelos testes deve ser sempre considerada como uma boa fonte de consulta para o conhecimento da aplicação.

Um comentário:

Den disse...

mto bom! ^^

Estas postagens sobre o TDD estão cobrindo bem o básico, ressaltando os principais pontos.

Continue com jogo pq está legal! =]