Como obter a API inicial correta usando TDD?


12

Esta pode ser uma pergunta bastante boba, como estou nas minhas primeiras tentativas no TDD. Adorei o senso de confiança que ele traz e a estrutura geralmente melhor do meu código, mas quando comecei a aplicá-lo em algo maior que os exemplos de brinquedos de uma classe, tive dificuldades.

Suponha que você esteja escrevendo uma espécie de biblioteca. Você sabe o que tem de fazer, conhece uma maneira geral de como ela deve ser implementada (arquitetura), mas continua "descobrindo" que precisa fazer alterações em sua API pública à medida que codifica. Talvez você precise transformar esse método privado em padrão de estratégia (e agora precise passar por uma estratégia ridicularizada em seus testes), talvez tenha extraviado uma responsabilidade aqui e ali e dividido uma classe existente.

Quando você está aprimorando o código existente, o TDD parece realmente bom, mas quando você está escrevendo tudo do zero, a API para a qual você escreve testes é um pouco "embaçada", a menos que você faça um grande projeto com antecedência. O que você faz quando já possui 30 testes no método que teve sua assinatura (e para essa parte, comportamento) alterada? São muitos os testes a serem alterados após a adição.


3
30 testes em um método? Parece que esse método tem muita complexidade ou você está escrevendo muitos testes.
Minthos 07/12/12

Bem, eu posso ter exagerado um pouco para expressar meu ponto de vista. Depois de verificar o código, geralmente tenho menos de 10 métodos por teste, com a maioria abaixo de 5. Mas a parte "voltar e trocá-los manualmente" tem sido bastante frustrante.
Vytautas Mackonis 07/12/12

6
@Minthos: Eu consigo pensar em 6 testes que qualquer método que utiliza uma string geralmente falha ou apresenta um desempenho ruim na gravação do primeiro rascunho (nulo, vazio, por muito tempo, não localizado adequadamente, com baixa escala de desempenho) . Da mesma forma para os métodos de coleta. Para um método não trivial, 30 parece grande, mas não muito realista.
Steven Evers

Respostas:


13

O que você chama de "grande design antecipadamente" , chamo de "planejamento sensato da sua arquitetura de classe".

Você não pode aumentar uma arquitetura a partir de testes de unidade. Até o tio Bob diz isso.

Se você não está pensando na arquitetura, se o que está fazendo é ignorar a arquitetura e colocar os testes juntos e fazê-los passar, está destruindo o que permitirá que o edifício permaneça ativo, porque é a concentração no estrutura do sistema e decisões sólidas de projeto que ajudaram o sistema a manter sua integridade estrutural.

http://s3.amazonaws.com/hanselminutes/hanselminutes_0171.pdf , Página 4

Eu acho que seria mais sensato abordar o TDD a partir de uma perspectiva de validação do seu projeto estrutural. Como você sabe que o design está incorreto se você não testá-lo? E como você verifica se suas alterações estão corretas sem alterar também os testes originais?

O software é "flexível" precisamente porque está sujeito a alterações. Se você não se sentir à vontade com a quantidade de alterações, continue ganhando experiência em design de arquitetura e o número de alterações que você precisará fazer nas arquiteturas de aplicativos diminuirá com o tempo.


O problema é que, mesmo com o "planejamento sensato", você espera que muita coisa mude. Normalmente, deixo intactos cerca de 80% da minha arquitetura inicial, com algumas mudanças no meio. Esses 20% são o que está me incomodando.
Vytautas Mackonis 07/12/12

2
Eu acho que essa é apenas a natureza do desenvolvimento de software. Você não pode esperar acertar toda a arquitetura na primeira tentativa.
Robert Harvey

2
+1 e não é contrário ao TDD. O TDD começa quando você começa a escrever o código, exatamente quando o design termina. O TDD pode ajudá-lo a ver o que você perdeu no seu design, permitindo refatorar o design e a implementação e continuar.
Steven Evers

2
Na verdade, de acordo com Bob (e eu concordo com ele de todo o coração), escrever código também é design. Ter uma arquitetura de alto nível é definitivamente necessário, mas o design não termina quando você escreve seu código.
Michael Brown

Resposta realmente boa que atinge a unha na cabeça. Eu vejo tantas pessoas, a favor e contra o TDD, que parecem "sem grandes projetos na frente", pois "não projetam nada, apenas codificam" quando na verdade é um ataque aos estágios insanos de design em cascata da antiguidade. O design é sempre um bom investimento de tempo e é crucial para o sucesso de qualquer projeto não trivial.
Sara

3

Se você faz TDD. Você não pode alterar a assinatura e o comportamento sem conduzi-lo por testes. Portanto, os 30 testes que falharam foram excluídos no processo ou alterados / refatorados junto com o código. Ou agora estão obsoletos, seguros para excluir.

Você não pode ignorar o vermelho 30 vezes no seu ciclo de refatoração de vermelho-verde?

Seus testes devem ser refatorados ao lado do seu código de produção. Se você puder, execute novamente todos os testes após cada alteração.

Não tenha medo de excluir os testes TDD. Alguns testes acabam testando blocos de construção para chegar ao resultado desejado. O resultado desejado em um nível funcional é o que conta. Testes em torno de etapas intermediárias no algoritmo que você escolheu / inventou podem ou não ter muito valor quando há mais de uma maneira de alcançar o resultado ou você se deparou com um beco sem saída.

Às vezes, você pode criar alguns testes de integração decentes, mantê-los e excluir o restante. Depende um pouco se você trabalha de dentro para fora ou de cima para baixo e quão grandes medidas você dá.


1

Como Robert Harvey acabou de dizer, você provavelmente está tentando usar o TDD para algo que deve ser tratado por uma ferramenta conceitual diferente (ou seja: "design" ou "modelagem").

Tente projetar (ou "modelar") seu sistema de maneira bastante abstrata ("geral", "vaga"). Por exemplo, se você tiver que modelar um carro, basta ter uma classe de carro com algum método e campo vago, como startEngine () e assentos int. Ou seja: descreva o que você deseja expor ao público , não como deseja implementá-lo. Tente expor apenas funcionalidades básicas (ler, escrever, iniciar, parar, etc) e deixar o código do cliente elaborado (prepareMyScene (), killTheEnemy (), etc).

Anote seus testes assumindo essa interface pública simples.

Mude o comportamento interno de suas classes e métodos sempre que precisar.

Se e quando você precisar alterar sua interface pública e seu conjunto de testes, pare e pense. Provavelmente, isso é um sinal de que há algo errado na sua API e no seu design / modelagem.

Não é incomum alterar uma API. A maioria dos sistemas em sua versão 1.0 adverte explicitamente os programadores / usuários contra possíveis alterações em sua API. Apesar disso, um fluxo contínuo e descontrolado de alterações na API é um sinal claro de projeto ruim (ou totalmente ausente).

BTW: Normalmente, você deve ter apenas alguns testes por método. Um método, por definição, deve implementar uma "ação" claramente definida em algum tipo de dados. Em um mundo perfeito, essa deve ser uma ação única que corresponde a um único teste. No mundo real, não é incomum (e não está errado) ter poucas "versões" diferentes da mesma ação e poucos testes correspondentes diferentes. Com certeza, você deve evitar 30 testes no mesmo método. Este é um sinal claro de que o método tenta fazer muito (e seu código interno fica fora de controle).


0

Eu olho para ele da perspectiva do usuário. Por exemplo, se suas APIs me permitirem criar um objeto Person com nome e idade, é melhor que haja um construtor Person (nome da string, int age) e métodos de acessador para nome e idade. É simples criar casos de teste para novas pessoas com e sem nome e idade.

doug

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.