Sou relativamente novo no TDD e tenho problemas ao criar meu primeiro teste antes de qualquer código de implementação. Sem qualquer estrutura para o código de implementação, eu sou livre para escrever meu primeiro teste da forma que quiser, mas ele sempre parece estar manchado pela minha maneira Java / OO de pensar sobre o problema.
Por exemplo, no meu Github ConwaysGameOfLifeExemplo do primeiro teste que escrevi (rule1_zeroNeighbours), comecei criando um objeto GameOfLife que ainda não havia sido implementado; chamou um método set que não existia, um método step que não existia, um método get que não existia e, em seguida, usou uma declaração.
Os testes evoluíram à medida que escrevi mais testes e refatoramos, mas originalmente parecia algo assim:
@Test
public void rule1_zeroNeighbours()
{
GameOfLife gameOfLife = new GameOfLife();
gameOfLife.set(1, 1, true);
gameOfLife.step();
assertEquals(false, gameOfLife.get(1, 1));
}
Isso foi estranho, pois eu estava forçando o design da implementação com base em como eu havia decidido, nesta fase inicial, escrever este primeiro teste.
Da maneira que você entende TDD, está tudo bem? Parece que estou seguindo os princípios do TDD / XP, pois meus testes e implementação evoluíram ao longo do tempo com a refatoração. Portanto, se esse projeto inicial tivesse se mostrado inútil, estaria aberto a alterações, mas parece que estou forçando uma direção no solução iniciando dessa maneira.
De que outra forma as pessoas usam TDD? Eu poderia ter passado por mais iterações de refatoração começando com nenhum objeto GameOfLife, apenas primitivos e métodos estáticos, mas isso parece muito artificial.
fonte
Respostas:
Eu acho que esse é o ponto principal da sua pergunta: se isso é desejável ou não, depende se você se inclina para a idéia do codeninja de que você deve projetar antecipadamente e depois usa o TDD para preencher a implementação, ou a ideia da durron de que os testes devem estar envolvidos. conduzindo o design e a implementação.
Penso qual destes você prefere (ou onde cai no meio) é algo que precisa descobrir por si mesmo como uma preferência. É útil entender os prós e os contras de cada abordagem. Provavelmente existem muitas, mas eu diria que as principais são:
Pro Upfront Design
Design de condução de teste profissional
Ao criar sua implementação em torno de um cliente do seu código (seus testes), você obtém a adesão ao YAGNI praticamente de graça, desde que não comece a escrever casos de teste desnecessários. De maneira mais geral, você obtém uma API projetada para ser usada por um consumidor, que é o que você deseja.
A idéia de desenhar vários diagramas UML antes de escrever qualquer código e preencher as lacunas é boa, mas raramente realista. No Code Complete, de Steve McConnell, o design é famoso como um "problema perverso" - um problema que você não consegue entender completamente sem antes resolvê-lo pelo menos parcialmente. Junte isso ao fato de que o problema subjacente em si pode mudar com os requisitos variáveis, e esse modelo de design começa a parecer um pouco sem esperança. A condução de teste permite que você reduza um pedaço de trabalho de cada vez - no design, não apenas na implementação - e saiba que, pelo menos durante a vida útil de ficar vermelho para verde, essa tarefa ainda será atualizada e relevante.
Quanto ao seu exemplo em particular, como diz durron, se você adotou uma abordagem para conduzir o design escrevendo o teste mais simples, consumindo a interface mínima possível, provavelmente começaria com uma interface mais simples do que a do seu snippet de código .
fonte
Para escrever o teste em primeiro lugar, é necessário projetar a API que você implementará. Você já começou com o pé errado escrevendo seu teste para criar o objeto inteiro
GameOfLife
e usando isso para implementar seu teste.Dos testes práticos de unidade com JUnit e Mockito :
Seu teste não faz muita tentativa de criar uma API. Você configurou um sistema com estado em que toda a funcionalidade está contida na
GameOfLife
classe externa .Se eu escrevesse esse aplicativo, pensaria nas peças que quero construir. Por exemplo, eu posso fazer uma
Cell
aula, escrever testes para isso, antes de passar para o aplicativo maior. Certamente criaria uma classe para a estrutura de dados "infinita em todas as direções" necessária para implementar adequadamente o Conway e testá-lo. Uma vez feito tudo isso, eu pensaria em escrever a classe geral que possui ummain
método e assim por diante.É fácil encobrir a etapa "escreva um teste que falha". Mas escrever o teste com falha que funciona da maneira que você deseja é o núcleo do TDD.
fonte
boolean
, esse design certamente seria pior para o desempenho. A menos que precise ser extensível no futuro para outros autômatos celulares com mais de dois estados?Há uma escola diferente de pensamento sobre isso.
Alguns dizem: teste não compilando é erro - vá corrigir, escreva o menor código de produção disponível.
Alguns dizem: não há problema em escrever o teste primeiro, verificar se é uma droga (ou não) e depois criar classes / métodos ausentes
Com a primeira abordagem, você está realmente em um ciclo de refatoração de vermelho-verde. Com o segundo, você tem uma visão um pouco mais ampla do que deseja alcançar.
Cabe a você escolher a maneira como trabalha. IMHO ambas as abordagens são válidas.
fonte
Mesmo quando eu implemento algo de uma maneira "hack it juntos", ainda penso nas classes e etapas que estarão envolvidas em todo o programa. Então você já pensou nisso e escreveu esses pensamentos de design como um teste primeiro - isso é ótimo!
Agora, continue interagindo nas duas implementações para realizar esse teste inicial e, em seguida, adicione mais testes para melhorar e estender o design.
O que pode ajudá-lo é usar o Pepino ou similar para escrever seus testes.
fonte
Antes de começar a escrever seus testes, você deve pensar em como projetar seu sistema. Você deve gastar uma quantidade considerável de tempo durante a fase de design. Se você fez isso, não terá essa confusão sobre o TDD.
TDD é apenas um link de abordagem de desenvolvimento : TDD
1. Adicione um teste
2. Execute todos os testes e verifique se o novo falhar
3. Escreva um código
4. Execute testes
5. Refactor code
6. Repita
O TDD ajuda você a cobrir todos os recursos necessários que você planejou antes de começar a desenvolver seu software. link: Benefícios
fonte
Não gosto de testes de nível de sistema escritos em java ou c # por esse motivo. Observe o SpecFlow para c # ou um dos frameworks de teste baseados em Cucumber para java (talvez JBehave). Então seus testes podem se parecer mais com isso.
E você pode alterar o design do seu objeto sem precisar alterar todos os testes do sistema.
(Os testes de unidade "normais" são ótimos ao testar classes únicas.)
Quais são as diferenças entre as estruturas do BDD para Java?
fonte