TDD:O seu primeiro TDD em Java

olá Pessoal,

Mais um post da śerie Agiile.Hoje veremos como escrever o seu primeiro TDD em Java (não é “o meu…”, pq este já fiz rs ), na semana passada eu dei um overview dos principios de TDD, nessa vamos praticar da maneira mais simples de ser. No próximo post, mostrarei como faço TDD hoje, após de algumas  “aulas” com o Kent Beck e o dia-dia fui aprimorando.Mas, não poderia de fazer um post para quem está no 0x0 do primeiro tempo :). (não sei pq,mas eu adorei a imagem que temos no inicio do post, passo horas olhando pra ela hehe)

lets go…

O foco

O foco aqui é poder compartilhar com vocês  de modo prático como foi que aprendi TDD. Pois, ainda há muitos programadores que não conseguem entender o uso, ou usar no dia-dia(claro que há exceções onde TDD não se aplica ,porém quando aplicamos este  já comprovou que os resultados são indiscutíveis).

O Exemplo

Antes de tudo vou buscar ser  bem direto ao foco do post e todo o conceito TDD será deixando de lado blz? Qualquer dúvida da uma passada no primeiro post ou no velho Google.

O nosso exemplo é o algo muito comum e escolhi ele, porque eu lembro como se fosse hoje, que  foi a partir dele que o mundo Agile marcou minha vida profissional, pois foi onde conseguir a dar meus primeiros passos ao desenvovimento guiado por testes.Sendo assim,resolvi usá-lo aqui no post. (não é nada chique, desde da faculdade, o que mais vemos é algo como RG, CPF, Locadora ,Estoque etc). Porém, o objetivo aqui não é saber se você sabe validar um RG,CPF,CNPJ, Locadora  etc. E sim se consegue desenvolver usando TDD.

Não esquecer

TDD não é apenas criar unit tests e sim ter testes inteligentes, ou seja, automatizados o suficiente para gritar quando alguém quebrar alguma regra. Outro detalhe não é pq são unit tests que você escreve de qualquer forma, é code então os principios são os mesmos utilizados no código funcional(não sei porque tem gente que separa isso, até hoje não entendi o motivo), na verdade quem desenvolve utilizando TDD tem um carinho enorme com seus testes, pois sabemos da importância deles durante o desenvolvimento e na manutenção. Já vi perguntas do tipo:

– pq não coloca tudo em um teste só? (será que precisa responder o motivo?)

– o que é um bom teste? (essa foi uma boa pergunta)

Primeiro que não há receita de bolo, mas um bom teste ele sempre tem um perfil parecido, grita de forma eficiente, ou seja, não grita por qualquer coisa que mudou, só grita quando algo que ele testa de fato mudou. Qual a diferença? Muita, há testes criados que tem varias responsabilidades e isso já passa ser um problema maior. Daí algo mudou e ele falha, mas a falha daria um novo teste especifico, mas o desenvolvedor que só escreve teste por escrever ou pq é mandatório no projeto, e importante que o teste esteja lá como métrica.

Enfim, Um bom teste deve ser especifico e rápido, assim vc pode executá-lo várias vezes.Isso é algo primário que precisamos ter em mente ao construir nossos testes.

Praticando

No exemplo a seguir temos uma aplicação que faz a “validação” de um nro de uma carteira de identidade. Este é um start-up para você ficar brincando após brincando após a leitura, mas terá que ser persistente e ir mais adiante para olhar o que tem  após omuro. Se não fizer foi ai onde quase todos desistiram.

Primeiro passo

Escrevemos a classe de teste e já fizemos o teste para os possíveis RG inválido/válido.

public class RGTest extends TestCase {

private RG validarg = new RG();

publicvoid testIsValidaRG(){

assertFalse(“retorna FALSE – inválido RG”, validarg.isValidaRG(“128641011”));

assertFalse(“retorna FALSE – RG inválido”, validarg.isValidaRG(null));

assertFalse(“retorna FALSE – RG inválido”, validarg.isValidaRG(“”));

assertTrue(“retorna TRUE – RG válido”, validarg.isValidaRG(“128640-28”));

}

Se for executado vai falhar porque não temos a classe principal “RG” que precisamos implementar. A falha anterior é de compilação, caso você execute.

public class RG {

publicboolean isValidaRG(String rg){

return false;

}

Segundo Passo

Agora precisamos testar e ver se vai falhar, pela teoria deve falhar.

O motivo da falha é que o método isValidaRG(String rg) sempre retorna false, então, nosso teste não está funcionando 100%. Pois, precisamos ter um nro de RG válido.

Terceiro passo

Fazer o teste funcionar, precisamos resolver o problema de ter um RG válido, para isso precisamos alterar o método da classe principal.

public boolean isValidaRG(String rg){

if((rg== null) || (rg.length()!=11)|| (rg.isEmpty()) || rg.charAt(8)!= ‘-‘){

return false;

}

return true;}

Agora estamos validando, os nossos assertXXX da classe teste.

O Bug

O código abaixo passa no teste, observe que não há nenhuma regra validando letras, e sabemos não existe RG com letras. Precisamos criar um teste para isso. E resolver o bug

assertTrue(validarg.isValidaRG(“abcdefgh-ij”));

Quarto passo

criamos um método na classe RGTeste para testar se de fato nossa aplicação NÃO aceita letras, mas pelo resultado abaixo, vimos que aceita, uma vez que o método retorna TRUE, já que não há nada que proíba as letras na classe principal.

//criando um novo método para testar letras

publicvoid testIsValidaRGLetras(){

assertFalse(“retorna FALSE – inválido letras RG”, validarg.isValidaRG(“ABCDEFGH-IJ”));

assertFalse(“retorna FALSE – Inválido letras RG”, validarg.isValidaRG(“G3X8Xopa-22”));}

 Quinto passo

Precisamos agora alterar isso no código principal, para testar que letras não podem passar pelo metodo.

public boolean isValidaRG(String rg){

if((rg== null) || (rg.length()!=11)|| (rg.isEmpty()) || rg.charAt(8)!= ‘-‘){

return false;

}//fim do if

for (int i = 0; i < rg.length(); i++) {

if(i!=8){

char posicao = rg.charAt(i);

//senao for umdigitoretorne false

if(!Character.isDigit(posicao)){

return false;

}}

}return true;

}

Ambos testes passaram, assim sabemos que a validação de RG está correta, até agora. Porém, o que devemos observar que nessa mecânica, fomos escrevendo o nosso código com base nos resultados dos unit tests primeiro e não fazer o inverso. É obvio que falta muito trabalho ainda até termos um RG realmente válido e inválido, mas lembre-se que o básico de TDD é querer fazer a barra verde chegar o quanto antes com o menor esforço possível, é um tal de baby-step. É comum, queremos implementar logo a parte complexa do código, querendo fazer as validações possíveis etc. Mas, é ai que o terreno está propicio para nascer os bugs mais terríveis de nossa vida e só saberemos quando o cliente abrir. Encontrar bugs em modelo como waterfall não é uma tarefa fácil, não é a toa que o custo de manutenção nesse modelo é alto. Lembre-se que devemos desenvolver algo que qualquer outro desenvolvedor possa entender no menor tempo possível, e TDD possibilita isso, eu particularmente, quando pego um código feito com TDD olho primeiros os testes. Os testes sãos os guias que precisamos. :).

 Meu case

No exemplo do post vimos como usar TDD, quando iniciei meus estudos nunca parece vantagem (acho que você deve está com esse sentimento também), olhando apenas a execução e o resultado. Mas, confesso que só conseguir ver a essência de TDD em partes diferente do projeto, uma quando precisamos alterar algo, e manter os testes passando, ou quando recebia novos requisitos e tinha que implementar e criar mais testes, ai vi agilidade em pratica e ficando viciado, sem falar que todo time fica feliz, pelo tempo gasto na manutenção, TDD de fato dar contribuição dele na manutenção de software. Fora que com o tempo vamos vendo o quanto TDD ajuda na construção do nosso design. Daí passamos a ter um tal de design incremental.

Espero que tenham gostado do post, vou ficando por aqui, até o próximo post.

Abracos, see ya!!

7 comentários em “TDD:O seu primeiro TDD em Java”

  1. Todo teste escrito deve falhar primeiro? Se eu escrever um teste (ou alguns), fazer eles passarem e nos próximos testes eu receber sinal verde, significa que eu estou fazendo errado? O ciclo da imagem do post deve ser sempre o mesmo para cada teste novo?
    Valeu.

    1. olá Willian,

      Por natureza o seu teste vai falhar no inicio primeiro até pq vc não tem nada implementado certo? Se não falha de primeira, é pq vc já tem codigo funcional desenvolvido primeiro. Não entendi perfeitamente as outras perguntas. Poderia refazer, por favor?
      abracos,

  2. Olá Camilo,

    Parabéns pelo post, estou começando a ler algumas coisas sobre TDD e estou achando muito bacana, mas gostaria de tirar uma dúvida contigo.

    Estou fazendo estágio como tester e uns dias atrás comecei a participar na resolução de tickets(problemas, implementações novas, etc).

    Eu já sabia mais ou menos como o TDD funcionava, mas nunca havia praticado, estou praticando umas coisas e penso em fazer um refactoring inteiro em um projeto meu, projeto bobo, mas que eu estou tentando juntar tudo o que estou aprendendo, focando sempre nas mudanças que valem a pena.

    Nesse caso em específico, o que eu reparei e acho que seria uma pedreira de implementar os testes unitários no projeto da empresa, seria pelo motivo de ser muito grande e já existir por muito tempo, se não me engano, 10 anos pra mais. Nesse caso, não pensei em desde o “genêsis” o projeto, porque estaria sem condições, mas quando eu tenho diversas classes já criadas, tais como validadores, consultas ao banco, factories e mais um monte de coisa, eu escrevo esse teste simples de inicio e conforme vai evoluindo a refatoração, eu consigo ter uma métrica boa do projeto funcionando como um todo.

    O que acho mais interessante nisso é não ter aquele negócio de ter que criar um método main pra testar um “pedaço” só da minha implementação e apagar, ou testar acessando a aplicação e vendo o que aconteceu por debug(meu caso e dos outros programadores), claro que isso ajuda, mas por exemplo, o que eu citei do main é bem verdade, eu faço isso as vezes, quero testar uma coisa, por exemplo, uma conexão no banco para buscar algo, logo ou eu crio um main que morre ali, porque depois eu apago e se deixar fica um monte de lixo que demora tempos para rodar, ou entro no sistema, sigo os passos e vejo pelo debug tudo. Acho que os dois passos são necessários, desde que eu tenha esse conjunto de testes e posso rodá-los a vontade, assim como todos os programadores envolvidos possam fazer as implementações/correções vendo os meus testes, jpa que o projeto fica sincronizado via repositório SVN, algo que eu vejo que seria legal, pois o TDD não “mata” o tester em si, ele precisa existir por outros motivos, mas que a primeira parte não seja problemática, do tipo, arrumar uma coisa e surgir outra e/ou voltar o mesmo problema por motivos assim que mencionei.

    P.S: Me perdoe escrever tanto assim, é porque me animei e as dúvidas prefiro explicar de uma forma mais extensa, obrigado pela atenção.

    1. olá Lindomar,
      projeto legados e com mais 10 anos é bem complicado e caro fazer refactoring e nesse caso tu n vai praticar TDD na verdade vai criar unit tests para codigos já existentes até dá para fazer TDD no novo code que vai nascer a partir do refactoring.
      Mas, qual era tua duvida mesmo? n ficou claro 🙂
      abracos,

  3. A dúvida era nisso, de como fazer TDD em projetos antigos com muito código e funções existentes, eu queria tentar propor pro pessoal isso, de aplicar testes para novas implementações.

    Eu achei muito legal fazer isso tudo, é uma garantia muito maior do que escrever o código, fazer um teste básico e todas as outras modificações são deixadas para trás, aí quando acontece algum problema, você “volta” muito atrás para encontrar algo, mesmo com debugger e tudo mais, algo que podia ser melhor visto nos testes, acho que era isso que eu estava pensando no momento, obrigado por responder.

Deixe um comentário para Willian Cancelar resposta

O seu endereço de e-mail não será publicado.