TDD – Desenvolvimento Orientado a Testes (Test Driven Development) é uma técnica de desenvolvimento de software relacionado aos conceitos de XP (Programação Extrema). que baseia em um ciclo curto de repetições.
O ciclo de desenvolvimento com TDD se resume em:
1 – Primeiro crie os testes unitários.
2 – Crie o código até que os testes funcionem.
3 – Refatore o código.
TDD é uma das formas de manter seu código funcionando seguramente mesmo que sejam feitas mudanças radicais no projeto. O primeiro passo, onde é requisito escrever um teste antes de ter o código final desenvolvido no projeto, certamente é o ponto mais difícil de entendimento entre profissionais e equipes que ainda não conhecem esta técnica.
O fato é que nesta primeira etapa é justamente onde você vai entender dentre muitas outras coisas, que terá uma regra seguramente funcionando antes de partir para o desenvolvimento do código.
Como assim? Você está me dizendo que “do nada” eu tenho que codificar uma regra?
Você ainda pode estar se perguntando: “Se eu ainda não tenho o código que funciona, como terei um teste para ele?”
Eu lhe digo, justamente! A regra no caso do TDD é justamente ter um teste que ainda não funcione, para que você passe para a segunda etapa que é “Criar um código que faça seu teste funcionar”, certo?
Para que isto se torne possível, neste texto irei falar do uso do framework DUnitX.
DUnitX é um framework open-source que pode ser utilizado no Delphi a partir da versão 2010. Ele é baseado em frameworks como NUnit e xUnit, contendo inúmeros métodos para implementação de testes unitários de suas classes e métodos. DUnitX utiliza métodos anônimos e generics. Existem muitos artigos e videos com exemplos práticos a serem explorados, a princípio você pode conhecer um pouco mais sobre este framework, conceitos e métodos a serem utilizados em: http://docwiki.embarcadero.com/RADStud…/…/en/DUnitX_Overview
Ok, mas voltando ao texto, isso pode não está bem claro ainda, então vamos ao tal código do teste.
Antes quero lhe dizer que cada teste, deve ser baseado em uma regra compreendida pela análise do problema a ser resolvido, então vamos ver um exemplo comum para iniciar.
Caso do teste:
Ao iniciar uma venda, em seguida finalizar a mesma venda sem itens lançados, o sistema deverá informar o usuário por diálogo de exceção, com a seguinte mensagem “É necessário pelo menos um item lançado para finalizar a venda”.
Observe a estrutura das classes de Venda. Note que não existem implementações desta regra de negócio na classe TVenda.
Mas isto não impede a criação dos métodos de teste, pois é justamente o que deve ser feito quando se trabalha com TDD, Desenvolvimento Orientado a Testes!
Agora vamos ver como é feito o Teste:
E o que este teste quer dizer mesmo?
Simples! Vamos ver cada parte demonstrada por cores.
Verde: Este método Setup serve para que seja executado algo para cada método de teste, isto ajuda para que você separe a parte de inicialização de variáveis e instancias de objetos e deixe seu código somente com a regra a ser testada.
Vermelho: É outro método a ser executado, desta vez após todo método de teste que for implementado. Assim como o Setup, neste método, você pode liberar possíveis objetos da memória e até executar outros procedimentos como finalizar uma conexão por exemplo.
Amarelo: Este é o primeiro método de teste conforme nosso caso relatado acima!
E como foi implementado este teste?
No framework de testes DUnitX, existe a classe Assert que contém inúmeros métodos para que possa ser feito várias condições, para nosso caso, foi utilizado o método WillRaiseWithMessage (a grosso modo quer dizer: Irá(deverá) levantar uma exceção contendo a seguinte mensagem). Para este método, podem ser passados os seguintes parâmetros:
1 – Um procedimento anônimo com uma implementação que deverá ser a mesma utilizada no sistema, ou seja, Inicia uma venda e Finaliza uma venda, neste caso sem lançar nenhum item, assim como previsto no Caso de teste.
2 – Uma classe de exceção, pode ser nil caso não tiver nenhuma específica.
3 – A mensagem que será esperada pelo sistema ao ocorrer o caso do teste. Esta mensagem tem que ser exatamente a mesma, caso contrário o teste irá falhar.
Pronto! Assim concluímos a primeira etapa do teste, este teste já pode ser executado e deverá dar errado, é isso que precisamos fazer até o momento.
Olhe o resultado ao executar o teste:
Pode comemorar, o teste falhou!
Veja só o que temos, o retorno de que o teste FinalizarVendaSemItensLancados não retornou um exceção como esperado!
Agora sim já podemos partir para o segundo passo que é justamente fazer isto acontecer em nossa classe, consequentemente na aplicação onde ela será utilizada.
O que precisamos fazer é simplesmente codificar nossa classe para que dispare uma exceção com a mensagem esperada pelo teste, então observe um exemplo de como isto pode ser feito na classe TVenda:
Assim, ao chamar o método Finalizar, será verificado se existem itens lançados, caso não existam é disparado a exceção com a mensagem correspondente.
Então ao executar novamente a aplicação de testes, devemos receber o seguinte resultado.
A partir deste modelo, você já pode criar outros testes unitários para sua classe, não há limites para isto, o ideal é que sejam atendidos 100% dos possíveis casos de sua aplicação, quanto mais casos forem atendidos melhor!
Bom, isto está longe de ser um material completo sobre testes unitários, porém já é o suficiente para que você conheça como irá finalmente garantir que sua aplicação se torne segura independente da mudança sofrida durante a vida do projeto
Para finalizar quero ressaltar alguns benefícios ao adotar TDD:
- Certeza de que se algum outro desenvolvedor acidentalmente mudar a regra de negócio na aplicação, o teste irá falhar antes que chegue ao cliente.
- Conhecimento de todas as regras de negócio ao ler os casos de testes.
- Identificar rapidamente regras incorretas ou obsoletas.
- Facilidade de alocação de um novo profissional para o projeto sem maiores riscos de mexer em código que já estava funcionando.
- Identificar quais são os mínimos e máximos argumentos para que regras de negócio sejam atendidas (eliminando redundância e métodos desnecessários).
- Documentação de regras de negócio, baseada nos casos de testes.
- Confiança!
É isso ai, muito obrigado por ler este artigo e não para por aqui, estou editando alguns videos onde certamente falarei mais sobre testes :d
Obrigado.
Carlos Eduardo – Delphi Clean Code