[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.