TDD - Curva de aprendizado

[quote=Marky.Vasconcelos]
Eu não vi diferença no exemplo da media do que eu fiz para o que voce postou, o TDD ajuda a projetar, mas você não vai precisar ir na wikipedia para fazer algo que já sabe. Nesse caso acredito que voce esteja seguindo a propria arquitetura que já esperava.[/quote]
Eu já falei sobre isso ai em cima:

Você pode definir o número de passo que vai dar em TDD, então no exemplo da média posso fazer exatamente como você falou até mesmo sem a triangulação, mas a finalidade do teste não é garantir a qualidade. A finalidade principal do teste é projetar e ter o feedback positivo, garantir a qualidade é uma consequência disso.

[quote=Marky.Vasconcelos]
E o fato de criar a logica no teste já deixa uma armadilha, se voce copia-la para dentro do método que quer testar, claro que ela vai ser testada com sucesso, mas é por que o teste só compara valores e não a logica em si.[/quote]
A ideia não é fazer um copy/paste e sim e projetar e partir disso desenvolver a logica. Em TDD existe remover a duplicação então se você copia e cola depois tem que remover a duplicação e mesmo assim se existe duvida pode usar a triangulação. O use-first/create-later apresenta o mesmo problema pois você também pode fazer um copy/paste!

TDD também não é infalível, não é bala de prata, então você cometerá erros que não serão cobertos pela base de testes e as vezes até erros de lógica que os testes não pegaram

A próxima vez que alguém me perguntar a diferença entre um mock e um stub, vou entregar o link dessa mensagem… :wink:

[quote=Marky.Vasconcelos]Eu já disse, não uso “TDD” e os projetos de testes aqui estão fechados a meses.

Eu sigo a filosofia use-first/create-later sem testes, isso me ajuda a terminar macro features sem ter implementado toda logica necessaria.

E já que o que eu aprendi não é TDD, vou procurar para ver se existe um nome ou então criar um eu mesmo :wink:
[/quote]

Isso que vc faz se chama Test First, que não é a mesma coisa que TDD apesar do muita gente acha.
O que o x@ndy está tentando demonstrar é como se usa o TDD sem cair num simples Test First, mas ele está sendo traído pelo exemplo que escolheu, que é trivial.

Faça o mesmo processo com algo mais complexo, como criar um builder para os Criteria do Hibernate, ou um builder para uma api de validação, ou um processo de negócio como a transferencia entre contas que considera o fee que o cliente paga e o tipo de cliente, ou algo que vc não saiba à priori como calcular seno usando BigDecimal em vez de double, ou calcula multiplicação de matrizes com multithread. Tem que ser algo desconhecido e com complexidade não trivial , pois senão não ha “desenvolvimento” envolvido e TDD precisa de “desenvolvimento” (Test Drive Development). É isso que o distingue do simples Test First.

Ai vc vai reconhecer os passos “baby steps” que o x@ndy está tentando exemplificar.

Eu acho que esse teste deveria ser:

  @Test
  public void testaMedia(){
    int valor1 = 10;
    int valor2 = 2;
    
    Media media = new Media(valor1, valor2);
    assertEquals(6, media.resultado())
  }

Aquele primeiro teste está duplicando o código testado, não? Você há de concordar que a implementação do código de produção fatalmente vai ficar assim

int media = (valor1 + valor2) / 2 return media; Acaba não testando realmente porque sempre vai estar igual!

Na segunda maneira o resultado não está calculado dentro do método de teste, mas veio do meu conhecimento sobre o domínio, sobre como o sistema deve se comportar. O código de produção é que tem a “obrigação” de fazer o cálculo que for necessário para chegar ao resultado correto.

[quote=gomesrod][quote=x@ndy]

  @Test
  public void testaMedia(){
    int valor1 = 10;
    int valor2 = 2;
    
    int mediaExperada = (valor1  + valor2) / 2
    
    Class media = new Media(valor1, valor2);
    assertEquals(mediaExperada, media.resultado())
  }

[/quote]

Eu acho que esse teste deveria ser:

  @Test
  public void testaMedia(){
    int valor1 = 10;
    int valor2 = 2;
    
    Media media = new Media(valor1, valor2);
    assertEquals(6, media.resultado())
  }

Aquele primeiro teste está duplicando o código testado, não? Você há de concordar que a implementação do código de produção fatalmente vai ficar assim

int media = (valor1 + valor2) / 2 return media; Acaba não testando realmente porque sempre vai estar igual!

Na segunda maneira o resultado não está calculado dentro do método de teste, mas veio do meu conhecimento sobre o domínio, sobre como o sistema deve se comportar. O código de produção é que tem a “obrigação” de fazer o cálculo que for necessário para chegar ao resultado correto.
[/quote]
Faço minha as palavras do Sergio:

[quote=sergiotaborda]
O que o x@ndy está tentando demonstrar é como se usa o TDD sem cair num simples Test First, mas ele está sendo traído pelo exemplo que escolheu, que é trivial.

Faça o mesmo processo com algo mais complexo, como criar um builder para os Criteria do Hibernate, ou um builder para uma api de validação, ou um processo de negócio como a transferencia entre contas que considera o fee que o cliente paga e o tipo de cliente, ou algo que vc não saiba à priori como calcular seno usando BigDecimal em vez de double, ou calcula multiplicação de matrizes com multithread. Tem que ser algo desconhecido e com complexidade não trivial , pois senão não ha “desenvolvimento” envolvido e TDD precisa de “desenvolvimento” (Test Drive Development). É isso que o distingue do simples Test First.

Ai vc vai reconhecer os passos “baby steps” que o x@ndy está tentando exemplificar.[/quote]
Esse exemplo realmente é simples e em todos os post coloquei que talvez nem fosse necessário criar um teste para isso! Foi somente para exemplificar que o teste serve para projetar a lógica e muitas vezes isso é composto até por mais de um teste mas você fala algo interessante:

Se você conhece o dominio e sabe como resolver o problema é só quer feedback positivo isto está certo (e na maioria das vezes vai ser assim) mas e caso você não soubesse como implementar isso, imagine uma logica muito mais complexa, como você faria? Outro problema da sua abordagem é que não existe diferença entre fazer o teste antes ou depois pois se você sabe como o sistema deve se comportar qual a diferença de implementar a lógica e depois testa-la de modo somente a saber que não cometemos nenhum erro?

Estou sem tempo agora, mas vou tentar fazer um exemplo mais complexo e postar no meu blog.

Mas mesmo que eu não saiba como implementar não tenho que saber de antemão qual o resultado esperado na operação? Calculado manualmente, ou por outra forma que confio que esteja correta?

Legal :!:

Não! Você vai descobrir o resultado implementando o teste! Isso é muito estranho mas essa é a grande sacada. A cada passo que você dá vai construindo a lógica que vai te levar ao resultado! Vejo muitos programadores fazerem “Teste de Mesa” para desenvolver uma lógica e o teste em TDD é muito parecido com isso. Ao invés de rascunhar algo no papel vou implementando no meu teste!

Claro que você não vai usar isso para tudo que for implementar pois, dependendo da confiança que você tem no que está fazendo, não é necessário. Já outras vezes é necessário um conjunto de testes simples para construir uma lógica mais complexa.

É complicado explicar bem como isso funciona! Eu mesmo só fui entender após ler o livro do Kent Beck, pois existem n conceitos envolvidos.

Digo que TDD é como orientação a objetos: muita gente acha que tem um sistema OO por que utiliza classes, polimorfismo e herança mas OOP é muito mais que isso assim como TDD é muito mais que crie o teste primeiro, barra vermelha, verde e refatore!

Eu acho que se deve ter muito cuidado com essas afirmacoes. Eu entendi a duvida do gamesrod.

Talvez não seja essa a tua intenção, mas você passa a impressão de que usa os testes para descobrir a regra, o que nem de longe é verdade. A regra você sabe qual é, você precisa saber.

Para fazer uma transferência bancaria eu preciso saber que 10 reais sairão da conta A para a B, não é possível eu descobrir isso durante os testes. O uso dos testes me levará ao como não ao o quê.

Como eu disse, talvez não seja a sua intenção, mas é isso que está parecendo.

Particularmente, eu uso testes como o gamesrod citou, sei que a media de 10 e 2 é 5, essa é a regra e isso eu preciso saber de antemão, seja o quão complexo for, então espero que meu teste me retorne 5. O teste, além de servir pra ajudar a definir o meu modelo, além de garantir que as coisas continuarão funcionando como eu espero, ainda me serve como documentação, então ele precisa ser limpo, legível e a intenção dele tem que ser clara.

[quote=YvGa][quote=x@ndy]
Não! Você vai descobrir o resultado implementando o teste! Isso é muito estranho mas essa é a grande sacada. A cada passo que você dá vai construindo a lógica que vai te levar ao resultado! Vejo muitos programadores fazerem “Teste de Mesa” para desenvolver uma lógica e o teste em TDD é muito parecido com isso. Ao invés de rascunhar algo no papel vou implementando no meu teste!
[/quote]
Eu acho que se deve ter muito cuidado com essas afirmacoes. Eu entendi a duvida do gamesrod.

Talvez não seja essa a tua intenção, mas você passa a impressão de que usa os testes para descobrir a regra, o que nem de longe é verdade. A regra você sabe qual é, você precisa saber.

Para fazer uma transferência bancaria eu preciso saber que 10 reais sairão da conta A para a B, não é possível eu descobrir isso durante os testes. O uso dos testes me levará ao como não ao o quê.

Como eu disse, talvez não seja a sua intenção, mas é isso que está parecendo.

Particularmente, eu uso testes como o gamesrod citou, sei que a media de 10 e 2 é 5, essa é a regra e isso eu preciso saber de antemão, seja o quão complexo for, então espero que meu teste me retorne 5. O teste, além de servir pra ajudar a definir o meu modelo, além de garantir que as coisas continuarão funcionando como eu espero, ainda me serve como documentação, então ele precisa ser limpo, legível e a intenção dele tem que ser clara.[/quote]

Pode ser que a palavra resultado não tenha sido bem empregada. Como resultado entenda-se o valor final da operação e não a regra de negócio. A finalidade do Teste é justamente testar a Regra! No caso da Média, qual seria a regra? Que a Média de 10 e 2 é 6? Como ficaria isso na minha lista de Teste? Qual a operação envolvida?

Para esse caso eu escreveria o seguinte na minha lista de teste: MEDIA = (X + Y)/2.

Note que o resultado da operação, nesse caso, não é importante (não que isso se aplique a tudo) e tanto faz se estou testando a media de 10 e 2 ou de 2232 e 4290. Sendo assim a asserção seria algo como assertEquals(mediaExperada, media.resultado()); Onde o mediaExperada seria o resultado do meu (X+Y) / 2.

Espero que tenha esclarecido.

[quote=YvGa][quote=x@ndy]
Não! Você vai descobrir o resultado implementando o teste! Isso é muito estranho mas essa é a grande sacada. A cada passo que você dá vai construindo a lógica que vai te levar ao resultado! Vejo muitos programadores fazerem “Teste de Mesa” para desenvolver uma lógica e o teste em TDD é muito parecido com isso. Ao invés de rascunhar algo no papel vou implementando no meu teste!
[/quote]

Eu acho que se deve ter muito cuidado com essas afirmacoes. Eu entendi a duvida do gamesrod.

Talvez não seja essa a tua intenção, mas você passa a impressão de que usa os testes para descobrir a regra, o que nem de longe é verdade. A regra você sabe qual é, você precisa saber.

Para fazer uma transferência bancaria eu preciso saber que 10 reais sairão da conta A para a B, não é possível eu descobrir isso durante os testes. O uso dos testes me levará ao como não ao o quê.

Como eu disse, talvez não seja a sua intenção, mas é isso que está parecendo.

Particularmente, eu uso testes como o gamesrod citou, sei que a media de 10 e 2 é 5, essa é a regra e isso eu preciso saber de antemão, seja o quão complexo for, então espero que meu teste me retorne 5. O teste, além de servir pra ajudar a definir o meu modelo, além de garantir que as coisas continuarão funcionando como eu espero, ainda me serve como documentação, então ele precisa ser limpo, legível e a intenção dele tem que ser clara.[/quote]

É aqui que está o problema. O que vc está exemplificando é Test First. O que o x@ndy está falando é TDD. O TDD é sim para encontrar a regra no sentido que vc não sabe como nem o quê é o resultado. Vc sabe o que lhe especificaram, ou seja o behaviour. No caso seria “o sistema deve calcular a média”. Como disse o exemplo é trivial pq vc já sabe como se calcula uma média, portanto não ha desenvolvimento envolvido, só implementação. Entao vc estabelece balizas conhecidas nos seus testes como a média de 10 e 2 é 6 que irão provar que sua implmentação não está errada. Isto é Test First. O test serve como arcabouço do resultado. Em TDD o teste serve como exploração do resultado. Imagine q vc tem que integrar com o Twitter e não ha uma API em java para isso. Vc sabe a especificação “ler o timeline do twiter”, mas vc não sabe como nem o quê. Ou seja, que código eu preciso e como eu preciso organizá-lo. Ai vc investiga, começa sabendo que ha um URL para acessar, ai vc aprende o protocolo, etc… isso é apenas informaçao, mas com ela vc vai montando seu codigo. Usa um objeot URL para fazer a coneção e ler e processa o HTML na mão. Blz. funcioana, mas não é elegante e não faz tudo o que vc precisa. Ai vc começa a melhorar o código e o design. Tlv vc deixe de usar o URL e passa a usar o HTTPClient da apache, etc… vc está desenvolvendo a sua API de conversa com o twiter e está usando o junit - por exemplo - como cliente dessa api, e explorando se é fácil ou dificil de usar, de entender , etc… Não é uma mero teste de unidade é todo um ciclo de desenvolvimento em que o teste é o client da aplicação/api.

Eu sei que nuance não é trivial e é bem complexo de explicar , mas se vc tem uma especificação , tem um teste para ela e simplesmente procura uma implementação, então é Test First. Se vc tem a especificação, mas não procura apenas uma implementação, vc procura a melhor implementação, o melhor design, etc… isso é TDD. A diferença é o ultimo D = Development. Desenvolver significa que vc ainda não sabe o que precisa ou ainda não melhorou o suficiente para atender outros requisitos além da especificação ( como qualidade, boas práticas, simplicidade, DRY, etc…)