Quão Baby-steps devo ser quando uso TDD?

Estou a estudar TDD a algum tempo e sempre vejo nos exemplos algumas coisas bem simples. Gostaria de saber se no ‘mundo real’ dos desenvolvedores acontece da mesma forma.

Por exemplo, depois de meus estudos, fui fazer uma calculadora usando um TDD (me aparenta ser um exemplo bem simples).
Eu teria a classe TestCalculator com alguns testes ali. O primeiro, seria testar a soma de 1 + 2, por exemplo. Na minha classe Calculator eu teria um método add, retornando um inteiro e recebendo dois. Eu retornaria 3, porque devo só passar aquele teste escrito.
Mesmo sabendo que pra outro teste (2 + 2) não passará, devo eu manter essa simplicidade e “inocência” de não escrever o método pra retornar a soma dos dois parâmetros? Digo, devo eu não codificar um return x + y (logo na primeira vez que escreverei o método) sabendo que se continuar retornando 3 dará errado? Eu sei que terei de refatorar no futuro, mas isso não toma tempo do desenvolvedor? Ou de outra forma, deveria eu não escrever um método de soma genérico já de primeira?

Creio que não escrevi minha dúvida de forma correta, mas se entenderem, gostaria de saber a opinião de vocês.

Depende do desenvolvedor.

Uma coisa boa é vc rodar os testes com alguma ferramenta que mostre cobertura de código.

Faça alguns testes e veja o que vc não esta exercitando. Por exemplo é comum termos testes que rodem o codigo de um try, mas não o do catch (até pq nesses casos usamos frameworks mais vigorosos, com a criação de Stubs e Mocks). Eu não pensaria em 100% de cobertura de codigo mas saber quantos % do codigo esta sendo testado é essencial.

E não esqueça também que código que joga uma exception não é coberto pela ferramenta, nem pelo emma ou pelo cobertura.

Bom, amanhã dou um exemplo pra ti mais real, agora são 1:51 da manhã e to pegradão rs

abraço

[quote=Andre Brito]Estou a estudar TDD a algum tempo e sempre vejo nos exemplos algumas coisas bem simples. Gostaria de saber se no ‘mundo real’ dos desenvolvedores acontece da mesma forma.

Por exemplo, depois de meus estudos, fui fazer uma calculadora usando um TDD (me aparenta ser um exemplo bem simples).
Eu teria a classe TestCalculator com alguns testes ali. O primeiro, seria testar a soma de 1 + 2, por exemplo. Na minha classe Calculator eu teria um método add, retornando um inteiro e recebendo dois. Eu retornaria 3, porque devo só passar aquele teste escrito.
Mesmo sabendo que pra outro teste (2 + 2) não passará, devo eu manter essa simplicidade e “inocência” de não escrever o método pra retornar a soma dos dois parâmetros? Digo, devo eu não codificar um return x + y (logo na primeira vez que escreverei o método) sabendo que se continuar retornando 3 dará errado? Eu sei que terei de refatorar no futuro, mas isso não toma tempo do desenvolvedor? Ou de outra forma, deveria eu não escrever um método de soma genérico já de primeira?

Creio que não escrevi minha dúvida de forma correta, mas se entenderem, gostaria de saber a opinião de vocês.[/quote]
Voce sem querer citou 3 abordagens que o Kent Beck descreve no livro de TDD (faking, triangulation e obvious solution). Todas elas fazem sentido no mundo real, mas sao dificeis de se entender com problemas simples como da calculadora.

Com faking, voce retorna o valor esperado e passa para o proximo teste (que deveria testar outra parte do contrato do seu metodo). Isso eh bom para ganhar tempo para pensar nas outras responsabilidades do metodo antes de partir para a implementacao final. No seu exemplo, significaria retornar 3 e partir para um teste com numeros negativos, ou de ponto flutuantes, e so depois disso implementar a solucao, que pode acabar nao sendo um simples +.

Ja na triangulacao, voce cria um segundo teste para sair da implementacao fake rapidamente. Isso eh bom para descrever com mais detalhes o metodo sendo implementado, quando isso eh necessario. No caso da soma, isso dificilmente seria preciso. (a maneira mais facil de se chegar a solucao seria pela eliminacao de duplicidade - a constante 3, sendo utilizada tanto no teste quanto no codigo)

E para os casos simples como da soma, voce pode partir para a solucao obvia. Nao ha motivo para escrever mais codigo se a solucao eh clara e bem descrita no teste. Isso eh facil de se julgar no caso da calculadora, mas na “vida real” eh preciso ser um pouco mais cuidadoso antes de usar esta abordagem.

Eh normal estranhar alguns aspectos do TDD, principalmente quando esta praticando. O que eu sugiro eh que depois da calculadora voce parta para um problema cuja solucao nao seja tao obvia e tente aplicar as 3 abordagens para sentir a diferenca entre elas. Saber quando e como usar cada uma delas eh o que vai realmente ser util no “mundo real” :slight_smile:

eu acredito que ai vai do bom senso, tipo, claro que a primeira implementação deve passar pelo test case, mas nesse exemplo me parece que não esta simplesmente passando por ele mas sim burlando-o (se você retornar 3 direto ao invés de somar 1 + 2). Intendo que você deva validar para um caso simples assim, sem burla-lo é claro, e depois você pegaria outros testes que poderiam barrar como nesse exemplo especifico, usar números negativos. Penso que isso ficaria mais “mundo real” (fora que essa “burlada” passaria uma falsa imagem de que está tudo funcionando), claro que isso é a minha opinião sobre essa forma de usar TDD…

Pessoal muito obrigado pelas respostas. De verdade mesmo, esclareceram um bocado.

Ivan, eu me esqueci desses três ‘elementos’. Eu li uma parte do livro do Kent Beck e lembrei dele quando você falou dessas três partes. Agora, por indicação de outro tópico, comecei a ler o livro do Lasse Koskela. Ele diz no começo do livro: “This book is about learning to take those small steps”. Parece que se encaixa bem nesse tópico de dúvidas.

De qualquer forma, eu sou um cara que nunca achei que livros fossem mais importantes que aprender na prática; fazendo, errando e entendendo o porque dos erros. Mas TDD é um negócio meio estranho pra quem está começando… Então acredito que lerei ele no decorrer dos dias úteis da semana e nos finais de semana poderei tirar um tempo pra fazer alguns exemplos práticos. O problema de aprender TDD sozinho (sem um dojo, por exemplo) é que nunca sabemos se estamos no caminho certo. Aconteceu isso com vocês também?

Anyway, grato pela ajuda de todos!

Um dos maiores drawbacks do TDD na minha opinião são justamente a adoção, pois é uma mudança forte na cultura de desenvolvimento e, outro problema é o “microdesign over macrodesign”. Se os testes não forem feitos de uma maneira simples e eficaz, tu vai ficar em certas “sinucas de bico”, como refatorando testes mais do que o teu próprio código.

Você tem que pensar sempre pra frente no teu código. As dicas que dou são:

  • Testes não devem ser bonitos, mas funcionais. Não precisa seguir as melhores práticas do mercado em testes unitários ou integração.
  • Faça o seu sistema funcionar, nem que seja uma versão JSE de algo JEE que tu tá desenvolvendo. Ex.: quero testar um one-table-per-class no hibernate. Faça um teste local, bem hardcode até tu ver ele funcionar. Uma vez isso estando pronto, simplesmente refatore e plugue no seu EJB.
  • Tente usar o máximo possível de POO, principalmente a dica de “composição ao invés de herança”. A composição ajuda muito quando tu precisas fazer os teus mocks funcionarem
  • TDD requer muito mais prática do que comer o livro do Kent Beck. Então, encare os fatos, tenta fazer, quebre a cabeça e faça o teu workaround pra ter o teu teste funcionando.
  • Quando eu sei que o meu teste está legal? Quando você consegue executar as funcionalidades do teu sistema automaticamente. Tipo, dou um play num testng.xml e ele executa a suite. Testes automatizados são a salvação para muitos sistemas.
  • Código 100% coberto é impossível e não significa que o seu sistema é perfeito (livre de bugs) e principalmente. Use a cobertura de forma sábia, não como última palavra do teu sistema
  • Sobre ferramentas, eu sugiro: TestNG para testes unitários/integração, Mockito para mocks, JMeter para carga, FindBugs (melhor ainda se for o Sonar) para qualidade de código e selenium para testes funcionais. UnitUtils tem algumas coisas interessantes também =)

Abraço

Olá

Excelentes as suas dicas. Para mim o principal problema é que o ensino de programação não começa com testes desde o hello World. Tem professor imbecil que soca padrões na cabeça do aluno mas não exige que TODOS os trabalhos sejam feitos com testes.

Eu, que sou macaco velho, tenho dificuldades com TDD e preciso forçar certas barras. Mas aquele que está em uma faculdade deve exigir dos professores que o ensinem a programa direito.

[]s
Luca

Existe algum local que ensine a fazer testes unitários? Com muitos exemplos e tal.
Livros como o JUnit in Action parecem que são voltados pra quem já conhece bem os testes. Eu vejo que o foco deles é ensinar a fazer testes em código não-trivial para testes (com SGBD, Servlets etc).