Está criando os objetos que você acha que precisará em um primeiro teste em TDD

15

Sou relativamente novo no TDD e tenho problemas ao criar meu primeiro teste quando chega antes de qualquer código de implementação. Sem nenhuma estrutura para o código de implementação, estou livre para escrever o meu primeiro teste, mas quero sempre, mas sempre parece sair maculado pela minha maneira Java / OO de pensar sobre o problema.

Por exemplo, no meu Github ConwaysGameOfLifeExample o primeiro teste que escrevi (rule1_zeroNeighbours) eu comecei criando um objeto GameOfLife que não tinha t sido implementado ainda; chamou um método set que não existia, um método de etapa que não existia, um método get que não existia e usou uma declaração.

Os testes evoluíram à medida que eu escrevi mais testes e refatorei, 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 pareceu estranho, já que eu estava forçando o design da implementação com base em como eu decidira, nesse estágio inicial, escrever este primeiro teste.

Da maneira que você entende o TDD, tudo bem? Eu pareço estar seguindo os princípios do TDD / XP em que meus testes e implementação evoluíram ao longo do tempo com a refatoração e, portanto, se esse design inicial tivesse se revelado inútil, estaria aberto a mudanças, mas parece que estou forçando uma direção solução, começando desta forma.

De que outra forma as pessoas usam o 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.

    
por Encaitar 17.03.2015 / 13:07
fonte

6 respostas

9

This felt odd as I was forcing the design of the implementation based on how I had decided at this early stage to write this first test.

Eu acho que este é o ponto chave na sua pergunta, se isso é desejável ou não depende se você se inclina para a ideia de codeninja que você deve projetar logo em seguida usar TDD para preencher a implementação, ou a idéia de durron que os testes devem envolver-se na condução do projeto, bem como na implementação.

Eu penso qual destes você prefere (ou onde você cai no meio) é algo que você precisa descobrir para si mesmo como uma preferência. É útil entender os prós e contras de cada abordagem. Provavelmente existem muitos, mas eu diria que os principais são:

Pro design inicial

  • Por muito bom que seja um processo TDD no design de condução, não é perfeito. TDing sem um destino concreto em mente às vezes pode acabar em becos sem saída, e pelo menos alguns desses becos sem saída poderiam ter sido evitados com um pouco de pensamento inicial sobre onde você quer acabar. Este artigo do blog faz esse argumento usando o exemplo do kata dos algarismos romanos e tem uma implementação final bastante agradável para mostre para isto.

Design Pro-Driving Pro

  • Ao construir sua implementação em torno de um cliente de seu código (seus testes), você obtém YAGNI-adherence praticamente de graça, contanto que você não comece a escrever casos de teste desnecessários. Mais geralmente, você obtém uma API projetada em torno do seu uso por um consumidor, que é basicamente o que você deseja.

  • A ideia de desenhar um monte de diagramas UML antes de escrever qualquer código e preencher os espaços é boa, mas raramente realista. No Código Completo de Steve McConnell, o design é notoriamente descrito como um "problema perverso" - um problema que você não pode entender completamente sem primeiro resolvê-lo pelo menos parcialmente. Junte isso ao fato de que o problema subjacente pode mudar por meio de mudanças nos requisitos, e esse modelo de design começa a parecer um pouco sem esperança. Teste de condução permite que você apenas morder uma parte do trabalho de cada vez em design, não apenas a implementação e saber que pelo menos para a vida útil de transformar vermelho para verde, essa tarefa ainda será atualizada e relevante. p>

Quanto ao seu exemplo em particular, como diz durron, se você optasse por uma abordagem de criação do 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 durron. seu snippet de código.

    
por 17.03.2015 / 15:17
fonte
17

Para escrever o teste, você deve projetar a API que será implementada. Você já começou com o pé errado ao escrever seu teste para criar o objeto inteiro GameOfLife e usá-lo para implementar seu teste.

Do teste prático de unidade com JUnit e Mockito :

At first you might feel awkward for writing something which is not even there. It requires a slight change to your coding habits, but after some time you will come to see it is a great design opportunity. By writing tests first, you have a chance of creating an API that is convenient for a client to use. Your test is the first client of a newly born API. This is what TDD is really all about: the design of an API.

Seu teste não tenta criar uma API. Você configurou um sistema com estado em que toda a funcionalidade está contida na classe GameOfLife externa.

Se eu fosse escrever este aplicativo, em vez disso, eu pensaria sobre as partes que eu quero construir. Por exemplo, posso fazer uma classe Cell , escrever testes para isso antes de passar para o aplicativo maior. Eu certamente criaria uma classe para a estrutura de dados "infinita em todas as direções" que é necessária para implementar adequadamente o Conway, e testar isso. Uma vez que tudo isso foi feito, eu pensaria em escrever a classe geral que tem um método main e assim por diante.

É fácil encobrir a etapa "gravar um teste que falha". Mas escrever o teste com falha que funciona da maneira que você quer é o núcleo do TDD.

    
por 17.03.2015 / 14:16
fonte
0

Existe uma escola diferente de pensamento sobre isso.

Alguns dizem: teste não compilação é erro - vá corrigir escreva o menor código de produção disponível.

Algumas pessoas dizem: não há problema em escrever teste primeiro, verifique se ele é uma droga (ou não) e crie classes / métodos ausentes

Com a primeira abordagem, você está realmente em um ciclo refator vermelho-verde. Com o segundo você tem uma visão geral um pouco mais ampla do que você quer alcançar.

Cabe a você escolher de que maneira você trabalha. IMHO ambas as abordagens são válidas.

    
por 17.03.2015 / 13:33
fonte
0

Mesmo quando eu implemento algo de uma forma "hack it together", ainda penso nas classes e etapas que estarão envolvidas em todo o programa. Então você pensou nisso e escreveu esses pensamentos de design como um teste primeiro - isso é ótimo!

Agora, continue interagindo com a implementação para realizar esse teste inicial e, depois, adicione mais testes para melhorar e estender o design.

O que pode ajudá-lo é usar pepino ou similar para escrever seus testes.

    
por 17.03.2015 / 14:08
fonte
0

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, você não terá essa confusão sobre o TDD.

O TDD é apenas uma abordagem de desenvolvimento link: TDD
1. Adicionar um teste
2. Execute todos os testes e veja se o novo falha. 3. Escreva algum código
4. Executar testes
5. Código de Refatoração
6. Repetir

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

    
por 17.03.2015 / 14:32
fonte
0

Eu não gosto de testes de nível sistema escritos em java ou c # por esse motivo. Veja o SpecFlow for c # ou uma das estruturas de teste baseadas em pepino para java (talvez JBehave). Então seus testes podem se parecer mais com isso.

Evocêpodealterarodesigndoseuobjetosemterquealterartodosostestesdosistema.

(testesdeunidade"normais" são ótimos ao testar turmas únicas.)

Quais são as diferenças entre as estruturas do BDD para Java? / a>

    
por 17.03.2015 / 18:00
fonte