Como introduzir TDD à minha vida?

Indo direto ao ponto:

Sou um Programador de Schrödinger e gostaria de introduzir testes e TDD à minha vida, à empresa que trabalho e nas que trabalharei. Infelizmente tenho aquelas eternas dúvidas:

Como começo a fazer testes partindo do ponto zero; e,

Como tornar isso um hábito, um desenvolvimento orientado a testes de fato?

Gostaria que a discussão tivesse um tema “Como introduzi TDD à minha vida”, queria que falassem do passado e do presente, desafios encontrados, uma opinião muito pessoal de como fizeram e como fazem isso. Eu aprendo muito por exemplo/tutorial de vida de outras pessoas.

Como vocês começaram com TDD? Agradeço desde já a ajuda.

Oi Bruno!

Sua pergunta é excelente e muito relevante. Eu nao vou argumentar em como começar, mas vou argumentar em relação aos meus próprios erros:

A pior maneira de tentar começar TDD é criando testes unitários em um projeto que já nasceu sem testes. Escrever testes unitários para código muito acoplado é praticamente impossível: eles viram pequenos testes de integração, já que você precisaria criar um número gigante de mocks para transformar o teste em unitário. E isso é um fato: código que nasceu sem testes é mais acoplado que o que nasce com testes! Quando você testa desde o começo, seu código é forçado a ser desacoplado, caso contrário você não conseguiria testa-lo! :slight_smile:

Em resumo: se você tentar implantar testes unitários em algo que já tem meses de desenolvimento, além de não conseguir realizar a tarefa direito, vai ficar frustrado e culpar o TDD.

Sim, isso é uma confissao de 4 anos atras :slight_smile:

Pois é, estou nessa também porque já faz muito tempo que não pego alguma coisa do zero. Sempre caio de para-quedas em projetos que tem coisas que me dão vontade de chorar.

Sou outro programador de Schrödinger.

Só um adendo…

Existem situações em que você precisa adicionar testes a um projeto existente, caso contrário você vai se dar mau. Para esses casos, é necessário uma pessoa com mais experiência em testes automatizados de um modo geral. E estou com o Paulo no que ele falou: Iniciante em TDD tentando aplicar em um projeto sem testes, vai se irritar e culpar o TDD.

Falando em testes unitários, como vocês definem uma “unidade”? Uma classe, um método?

[quote=Paulo Silveira]
Em resumo: se você tentar implantar testes unitários em algo que já tem meses de desenolvimento, além de não conseguir realizar a tarefa direito, vai ficar frustrado e culpar o TDD.[/quote]

Nao concordo com essa afirmacao.

Em muitas ocasioes vale a pena criar testes unitarios para projetos em andamento. Se vc esta trabalhando num projeto existente porque nao introduzir testes unitarios durante o refactoring ou para novas funcionalides do sistema?

Mas acho que vc quis se referir a introduzir testes unitarios para ser adotado por uma equipe de desenvolvimento e nao por um ‘desenvolvedor solo’. Isso e realmente mais complicado, pq se as pessoas estivessem dispostas a comprar a ideia ja teriam feito desde o inicio do projeto.

Excelente tópico.
Sempre tive a mesma dúvida, mas nunca tive coragem de perguntar :oops: :oops: (na verdade, nunca tive tempo pra estudar direito isso. Agora nas férias não tem como escapar).

Tópico favoritado.

Abraços.

Olá

A pergunta é muito pertinente e ainda tem o mérito de desencavar este excelente post do nosso amigo Chapiewski

Acho que ninguém aqui aprendeu a programar fazendo testes. A gente sabe que é bom e tudo o mais mas começar a programar pelos testes exige um certo esforço mental.

Eu que sou macaco velho e põe velho nisto, sinto dificuldades nas raras vezes em que escrevo código de pensar primeiro nos testes. Já andei me forçando mas como escrever código não faz parte do meu dia a dia ainda não peguei o hábito. É isto que cada um precisa: pegar o hábito. Para isto precisa investir um certo tempo sofrendo um pouco.

Sabem do que sinto falta? De um tutorial explicando como fazer isto usando o eclipse (ou o Netbeans) desde a criação e organização das classes de teste e das classes testadas. Alguns dos melhores links que conheço são também do Chapiewski:

http://gc.blog.br/2007/06/20/slides-da-palestra-sobre-tdd-no-riojug/

http://gc.blog.br/2008/03/30/test-infection-por-onde-comecar/

[]s
Luca

É, caio no mesmo caso do Victor, o código que pego é quase sempre de manutenção. Apesar disso não acho que esse é o maior problema.

Por aqui a arquitetura é o famoso BOLOVO, orientado ao banco de dados. O problema é que não sei testar algo onde o endpoint é sempre o BD, onde as operações são sempre “… do/no banco”, verificar se tal dado existe no banco, trazer várias coisas do banco, associá-las, e mostrar na tela, inserir, alterar… Os dados sempre vem e vão para algum lugar.

E não ajuda o fato que o mecanismo de persistência é fixo, SQL/JDBC na mão, mudar ele para um mock e de volta p/ o mecanismo real daria trabalho e é falível.

Ouvi falar que o melhor modo de tratar isso é instalar um SGBD localmente e zerar os registros antes de começar os testes.

TDD precisa de uma ambiente mínimo para funcionar?

[quote=Bruno Laturner]Indo direto ao ponto:

Sou um Programador de Schrödinger e gostaria de introduzir testes e TDD à minha vida, à empresa que trabalho e nas que trabalharei. Infelizmente tenho aquelas eternas dúvidas:

Como começo a fazer testes partindo do ponto zero; e,

Como tornar isso um hábito, um desenvolvimento orientado a testes de fato?

Gostaria que a discussão tivesse um tema “Como introduzi TDD à minha vida”, queria que falassem do passado e do presente, desafios encontrados, uma opinião muito pessoal de como fizeram e como fazem isso. Eu aprendo muito por exemplo/tutorial de vida de outras pessoas.

Como vocês começaram com TDD? Agradeço desde já a ajuda.[/quote]

Existe uma diferença entre usar testes unitários e usar TDD. TDD significa Test Driven Development : Desenvolvimento Guiado por Testes. Isso é radicalmente diferente do que simplesmente usar um Junit da vida e ter algumas ou todas as classes sobre o controle de testes ( cobertura 100%).

Então o ponto inicial é começar a usar testes unitários. O JUnit e o Eclipse são uma boa dobradinha, mas existem outras por ai (TestNG + Maven ou Ant por exemplo). O ponto aqui é simples. Vc faz uma classe que executa alguma logica. Vc sabe que passados os parametros x e y o resultado tem que ser z. Vc cria uma classe de test (do junit por exemplo) e executa o codigo como se estivesses dentro de um programa real. A diferença é que o seu método de teste só executa uma classe. testes corriqueiros que vemos em exemplos em revistas e afins não são realistas. ninguém escreve um teste para testar de 1+1 = 2.
Em aplicações normais as suas classes vão produzir objetos e alterar estados em outros objetos de outras classes e não simplesmente retorná-los como uma calculadora. Vão tentar enviar email, mensagens via Jms , acessar o banco, processar um arquivo. É isso que tem que ser simulado no teste. vários niveis de complexidade geram vários tipos de teste (unidade, integração, etc… ) comece pelos de unidade.

Rápidamente vc vai entender que aplicar testes de unidade em ambientes complexos demanda uma muito boa separação de responsabilidade dos seus objetos. Para fazer testes bem feitos o seu modelo tem que ter OO bem feita. O seu modelo vai tender naturalmente a ser desacoplado porque vc vai precisar substituir objetos reais por objetos simulados. Por exemplo, substituir um repositório que comunica com o banco por um que simplesmente guardas as coisas em memoria e disponibiliza métodos públicos para podermos consultar se os objetos estão lá e montar os asserts. Estes objetos ficticios, simulados - chamados mocks - podem levá-lo na grande estrada do teste orientado a mocks. Que nada tem a haver com TDD em uma primeira aproximação.

O habito vem do seu esforço em não desistir de incluir suas classes sobre o controle de testes. A maior parte das vezes isso não é simples e requer um esforço tanto de codificação como de refactoring para que possam ser injetados objetos simulados.

TDD tem um propósito diferente do simples teste. Você não modela a aplicação e depois a testa. Vc primeiro escreve código que usa o modelo na forma de testes. A classe do modelo não tem código, apenas interface. O teste invoca essa interface. O teste é executado e dá erro porque nada foi implementado ainda. Ai vc implementa a lógica da classe. Testa. refactora, Testa. E continua nisto até que o teste passe.
Ai vc parte para outra funcionalidades. Sempre começando por definir como o codigo se deve comportar e só depois implementando o miolo. O teste vem primeiro. É ele que guia o desenvolvimento da classe como um cliente dos serviços da classe e não a classe que dita o teste. É por isso que se chama Desenvolvimento guiado por teste (TDD). Em TDD raramente ha a necessidade de mocks porque vc não está simulando o funcionamento do sistema, vc está escrevendo como ele vai se comportar. Claro que mocks são uteis em situações complexas que acabarão aparecendo, mas não são o foco nem a ferramenta principal. TDD é muito ligado a design by contract. A preocupação é como o que a classe deve fazer e não como ela deve fazê-lo. Ela tem que respeitar o contrato.
O TDD incrementa isso definindo o contrato à priori via código executável ( o código do teste).

TDD é um processo moroso e pouco ágil para sistemas complexos.Para classes unitárias pode ser mais eficaz.
Embora isso, ele é altamente compensador uma vez completado já que vc acaba como a classe e o teste prontos ao mesmo tempo. Eu utilizo esta técnica apenas quando não domino aquilo que a classe irá fazer ou não tenho o modelo claro na minha cabeça, ou quero experimentar com várias interfaces para a API para encontrar a que é mais fluente e coerente. Assim , ao escrever o código cliente primeiro, as funcionalidades da classe (ou de uma pequena biblioteca de classes) aparecem mais naturalmente.


Como nota tenho que comentar a desconfiança encontrada nas palavras do Donald Knuth. Ele tem toda a razão no que diz. O problema é que, como sempre, ha uma confusão de conceitos básicos e os hypes levam a melhor. Orientação a Objetos não tem mistérios sobrenaturais.

Como o próprio Knuth diz nessa mesma entrevista “software methodology has always been akin to religion.” é preciso ter muito cuidado quando se fala de TDD , DDD , XP, etc … sem raciocinar a diferença entre a metodologia e a filosofia e as técnicas e ferramentas. As técnicas e ferramentas são as da OO. Não existem outras. Não serão criadas outras.

Aqui fica a ideia dele - expressa na mesma entrevista - sobre XP.

[quote] With the caveat that there?s no reason anybody should care about the opinions of a computer scientist/mathematician like me regarding software development, let me just say that almost everything I?ve ever heard associated with the term “extreme programming” sounds like exactly the wrong way to go…with one exception. The exception is the idea of working in teams and reading each other?s code. That idea is crucial, and it might even mask out all the terrible aspects of extreme programming that alarm me.
[/quote]

É tudo uma questão de bom senso. Metodologia não poderão nunca suplantar a boa e velha teoria de computação que lhes dá base.
Seja OO ou outra qualquer.

Quanto ao problema do gato do Schrodinger o nome não é muito bom. A MQ é um dos mais exatos ramos da física e não aleatória como é popular. O gato do Schrodinger é só um, não dois. O gato está morto ou vivo. Não morto e vivo. É por isso que quando vc abra a caixa nunca sai um gato zombie de lá. Ou sai vivo, ou está morto. (é como lançar uma moeda , ou sai cara ou coroa, nunca sai cara e coroa)
O problema mais grave com a analogia é que passa a idéia de que vc pode escrever qq código absurdo e por meio de escrever testes vc consegue que ele funcione corretamente (mascarando a incompetência do programador) e sem saber como isso veio a acontecer. Ou seja, faça-se um sistema caótico, inclua-se testes e vira um sistema estável. (!)
É verdade que os testes levam a essa conclusão, mas não sozinhos. É o refactoring e reimplementação durante os testes que leva o sistema a funcionar. Um programador mentecapto nunca irá colocar o programa a funcionar por muitos testes que existam.

Acho que é isso que fica das palavras do Knuth: parem de procurar nas metodologias o que falta em conhecimento fundamental.

[quote=Bruno Laturner]
Ouvi falar que o melhor modo de tratar isso é instalar um SGBD localmente e zerar os registros antes de começar os testes.

TDD precisa de uma ambiente mínimo para funcionar?[/quote]

A principio nao importa se vai ser em memoria, banco de dados local ou remoto desde que seja um espaco onde nao exista interferencias externas. Lembrando que testes unitarios, cada desenvolvedor tem seu processo de build isolado dos demais desenvolvedores no projeto.

Em se tratando do ambiente, eu concordo quando o sergiotaborda diz “TDD é um processo moroso e pouco ágil…” mas ao mesmo tempo é a estrategia mais eficaz que conheco pra desenvolvimento que envolva mais de 1 programador. Isso inclui code ownership, CI e tudo aquilo que TDD compreende alem de testes do desenvolvedor. Este ultimo principalmente acho util no modo solo mas TDD como todo nao oferece tantos beneficios evidentes.

Assino embaixo Luca, na minha opinião é uma falha. Se vários tutoriais viessem com testes unitários, bem como exemplos, ficaria muito mais fácil incorporar estes hábitos sem contar que seria vantajoso verificar como outros usam os testes e ainda ver como certas funcionalidades são testadas.

Seria uma grande mudança da forma de se programar, por mais que falem muito de TDD, sem exemplos a maioria acaba não se acostumando e desiste, sem contar que o esforço de usar algo pouco demonstrado aumenta consideravelmente.

E quando digo pouco demonstrado, não me referi a tuoriais de como usar TDD, isso existe e muitos os conceitos estão bem explicados, é relativamente fácil de entender o q se propões, mas o uso prático, ver os testes, ver como estão usando e resolvendo situações parecidas, comparar, e por isso para aprender através deles ainda falta muito.

Sempre que pego um sistema ou projeto para estudar, a primeira coisa que verifico são os testes unitários, e uma infima minoria se preocupa com eles, e muitos ainda são primários, testando coisas óbvias e deixando de lado testar a lógica de negócio.

Aqui tem ótima referências:

Ando estudando TDD esses ultimos dias, muito bom seu post. Mas fiquei meio confuso no “raramente”. Escrever como o sistema vai se comportar não indica o uso de recursos externos? Claro que a super utilização de mocks mostra que o que você esta escrevendo provavelmente está com um acoplamento muito alto, mas via de regra sempre há algum acoplamento. Esse “raramente” é tão “raramente” assim?

Ótima resposta Sérgio!

Eu já faço a distinção entre fazer testes e fazer TDD, meio que coloquei isso na frases grifadas. “Como começo a fazer testes” é o meu objetivo inicial, o começo de tudo, “Como tornar isso um hábito, um TDD de fato” é o meu objetivo final.

Concordo demais com que fala de exemplos de revistas, elas se focam demais em “testar que instrumentos musicais estão afiados”, pouco falam de testar se os músicos sabem tocá-los e ler as partituras, e nunca falam sobre como garantir que a orquestra ser um sucesso.

Outra, por acaso testar as partes garante que o todo funcione?

No caso que descreveste, não é demais ter ver se o email foi enviado certo, se a mensagem Jms chegou, verificar o que no banco mudou, de o resultado do processamento do arquivo está certo? Testar cada um em separado já não garantiria tudo?

Conseguirá se o que ele fizer passar em todos os testes; [color=green]All green[/color]. Como você disse “A preocupação é como o que a classe deve fazer e não como ela deve fazê-lo.”

Se bem que se ele consegui essa “façanha”, ele não será mentecapto.

Ando estudando TDD esses ultimos dias, muito bom seu post. Mas fiquei meio confuso no “raramente”. Escrever como o sistema vai se comportar não indica o uso de recursos externos? Claro que a super utilização de mocks mostra que o que você esta escrevendo provavelmente está com um acoplamento muito alto, mas via de regra sempre há algum acoplamento. Esse “raramente” é tão “raramente” assim?
[/quote]

Quando vc usa TDD vc não tem as classes ainda. Vc está criando-as e sendo guiado pelos testes para as criar.
É um processo de desenvolvimento de “dentro-para-fora” no sentido que coisas como banco e jms só vão chegar no fim.
Então, vc passa quase todo o desenvolvimento sem estar simulando nada. Vc está correndo o sistema como ele tem que ser.
No fim do desenvolvimento vc aplica a tecnica normal de testes e ai tudo tem que ser testado. É ai que entram os mocks. Mas isso acontece quer vc use TDD ou não.

O raramente vem de que, às vezes, em certas circunstancias, vc precisa de um mock durante o desenvolvimento. Sobretudo quando vc tem alguma comunicação com outro sistema ou mecanismo que vc ainda não conhece, etc… Por exemplo, vc invocar um webservice que não é seu. Ai vc é “forçado” a usar o mock durante o TDD.

E não, o como o sistema vai se comportar não indica uso de sistema externos. É relativamente simples abstrair esses sistemas utilizando classes especializadas e implementações delas que simplesmente simulam o real. Isso é naturalmente util ao mexer com banco de dados, por exemplo, em nunca durante o desenvolvimento vc usa um banco realmente. Vc usa uma classe(biblioteca) que simula o banco. Repare que o foco do TDD é desenvolver a aplicação (tal como outras DD por ai como MDD, DDD , etc…) e não desenvolver os testes em si mesmos. Eles são um sobre-produto que resulta naturalmente da metodologia.

Detalhe, desenvolvendo com TDD vc obtem testes. Não necessariamente vc obtém bons testes ( com a mesma qualidade e profundidade necessária aum teste que visa ter cobertura máxima, por exemplo)

[quote=Bruno Laturner]Ótima resposta Sérgio!

Eu já faço a distinção entre fazer testes e fazer TDD, meio que coloquei isso na frases grifadas. “Como começo a fazer testes” é o meu objetivo inicial, o começo de tudo, “Como tornar isso um hábito, um TDD de fato” é o meu objetivo final.

Concordo demais com que fala de exemplos de revistas, elas se focam demais em “testar que instrumentos musicais estão afiados”, pouco falam de testar se os músicos sabem tocá-los e ler as partituras, e nunca falam sobre como garantir que a orquestra ser um sucesso.

Outra, por acaso testar as partes garante que o todo funcione?
[/quote]

Não. Não garante. Porque aquele pedacinho de codigo que cola as partes tb pode ter erro. É por isso que existem testes de integração. Mais, as peças por si só funcionam, mas cada uma com logica diferente. Quando se unem elas não são compativeis conceptualmente (embora as suas interfaces de codigo sejam)

não, não é demais. Testar nunca é demais. Esse é um fator bom e ruim, já que é facil se desmoralizar por não ter testado tudo o que podia ( já que o que podia ser testado é muito vasto)
No caso do email, por exemplo, ele pertence a um processo. E é o processo que vc está testando, não o envio fisico do email ( isso o pessoal da API de JavaMail já fez) . Por exemplo, o usuario é cadastrado e a senha enviada por email. Se o email não é enviado corretamente o usuário não pode ser salvo (não pode dar o commit) porque assim teriamos um usuário sem acesso ao sistema. O mesmo ao contrario se o banco dá pau não ha pq enviar o email.
Mas quando vc testa isto vc não envia email para ninguem com JavaMail. Vc simula. E simula que o envio deu problema (lançando um exception).
Testes de integração ou de processo são tão ou mais importantes que os de unidade. são eles que realmente garantem o sistema.

[quote]

Conseguirá se o que ele fizer passar em todos os testes; [color=green]All green[/color]. Como você disse “A preocupação é como o que a classe deve fazer e não como ela deve fazê-lo.”

Se bem que se ele consegui essa “façanha”, ele não será mentecapto.[/quote]

O ponto é que se o teste dá vermelho e o programador é incompetente ele nunca o fará ficar verde. Não é adicionando mais testes que vai ficar verde, é treinando o cara. Enfim, o conhecimento do programador ainda é mais importante que os testes. Se o cara faz testes mas apenas porque é mau programador, isso não é uma boa utilização dos testes ( é como ficar fazendo debug de tudo o que vc escreve a cada linha que vc escreve pq vc não tem a certeza de que fez certo. )

Aí que eu queria chegar. Eu posso não usar mocks o tempo todo como eles são do lado do teste (mocks de biblioteca, como jMock), mas de alguma forma e em algum momento eu preciso simular meus recursos externos (como BD), e isso não é raro.

E uma classe que simula o real, no meu entendimento, não deixa de ser um mock também!

Obrigado!

Então Sérgio, por onde você começa um teste, como o faz no dia a dia? Como o caso de uso se transforma em testes?

Pergunto por que os casos podem ser muito gerais ou num nível muito alto, sendo que os testes mais unitários são aqueles que estão lá em baixo na cadeia, testando a base do sistema. Como você identifica as tuas unidades?

Disse que o desenvolvimento é de “dentro para fora”, mas é necessário ter uma boa visão de tudo antes de escrever teu primeiro teste.

Tem alguma macete/dica para começar com os testes ou é só com a experiência?

Olá

Este tópico está muito bom. Gostei muito dos links que passaram.

Antigamante, no tempo em que ninguém falava de testes, que os programas eram lotados de System.out.println, passei por aqui e quando o chique era o chamado desenvolvimento top down, eu muitas vezes discutia com os top downzistas argumentando que sentia necessidade de fazer coisas bottom up.

É claro que levava pedrada de todo jeito porque estava violando um princípio básico da época que era primeiro pensar, pensar, pensar e pensar mais um pouquinho e só depois começar a fazer. Mas o que sentia era necessidade de ver funcionando algoritmos que seriam posteriormente incorporados na solução. Era como testar as ideías. Ainda bem que hoje ninguém mais pensa em top down e outras cascatas.

Encaro o TDD mais ou menos do mesmo jeito. É poder partir sabendo que o principal vai dar certo.

Vantagens? A principal, como o Paulo salientou, é que o sistema redundante fica naturalmente mais desacopladinho e para mim mais fácil de entender.

E devemos testar tudo para obter 100% de cobertura? Isto já acho exagêro em muitos casos mas chegar perto é muito tranquilizador.

[]s
Luca