Estou tentando entender o TDD, especificamente a parte do desenvolvimento. Eu olhei para alguns livros, mas os que encontrei abordam principalmente a parte dos testes - a História do NUnit, por que o teste é bom, Vermelho / Verde / Refator e como criar uma Calculadora de Cordas.
Coisas boas, mas isso é "apenas" Teste de Unidade, não TDD. Especificamente, não entendo como o TDD me ajuda a obter um bom design se precisar de um Design para começar a testá-lo.
Para ilustrar, imagine estes três requisitos:
- Um catálogo precisa ter uma lista de produtos
- O catálogo deve lembrar quais produtos um usuário visualizou
- Os usuários devem poder procurar um produto
A essa altura, muitos livros tiram um coelho mágico do chapéu e apenas mergulham em "Testando o ProductService", mas eles não explicam como chegaram à conclusão de que existe um ProductService em primeiro lugar. Essa é a parte "Desenvolvimento" no TDD que estou tentando entender.
Precisa haver um design existente, mas coisas fora dos serviços da entidade (ou seja: Existe um Produto, portanto deve haver um ProductService) não podem ser encontradas (por exemplo, o segundo requisito exige que eu tenha algum conceito de Usuário, mas onde eu lembraria a funcionalidade? E a Pesquisa é um recurso do ProductService ou um SearchService separado? Como saberia qual devo escolher?)
De acordo com o SOLID , eu precisaria de um UserService, mas se eu projetar um sistema sem TDD, posso acabar com um monte de serviços de método único. O TDD não tem a intenção de me fazer descobrir o meu design em primeiro lugar?
Sou desenvolvedor .net, mas os recursos Java também funcionam. Sinto que não parece haver um aplicativo ou livro de amostra real que lide com um aplicativo de linha de negócios real. Alguém pode fornecer um exemplo claro que ilustra o processo de criação de um design usando TDD?
Respostas:
A idéia do TDD é começar com testes e trabalhar com isso. Portanto, para dar o seu exemplo de "Um catálogo precisa ter uma lista de produtos", pode ser visto como um teste de "Verificar produtos no catálogo" e, portanto, este é o primeiro teste. Agora, o que contém um catálogo? O que contém um produto? Essas são as próximas peças e a idéia é reunir algumas partes que seriam algo como um ProductService que nascerá da aprovação do primeiro teste.
A idéia do TDD é começar com um teste e depois escrever o código que faz o teste passar como o primeiro ponto. Os testes de unidade fazem parte disso sim, mas você não está olhando para a imagem geral formada começando com testes e depois escrevendo o código para que não haja pontos cegos nesse momento, pois ainda não há código.
Tutorial de desenvolvimento orientado a testes, onde os slides 20 a 22 são os principais. A idéia é saber o que a funcionalidade deve fazer como resultado, escrever um teste para ela e criar uma solução. A parte do design variará conforme o necessário, pode ou não ser tão simples de fazer. Um ponto importante é usar o TDD desde o início, em vez de tentar se apresentar tarde no projeto. Se você começar com os testes primeiro, isso pode ajudar e provavelmente vale a pena notar em um sentido. Se você tentar adicionar os testes posteriormente, isso se tornará algo que pode ser adiado ou atrasado. Os slides posteriores também podem ser úteis.
Um dos principais benefícios do TDD é que, começando pelos testes, você não está preso a um design inicialmente. Assim, a idéia é construir os testes e criar o código que passará nesses testes como uma metodologia de desenvolvimento. Um grande projeto inicial pode causar problemas, pois isso dá a idéia de travar as coisas no lugar, o que torna o sistema sendo menos ágil no final.
Robert Harvey acrescentou isso nos comentários, que vale a pena afirmar na resposta:
fonte
Pelo que vale, o TDD me ajuda a obter o melhor design mais rapidamente do que não o TDD. Eu provavelmente chegaria ao melhor design com ou sem ele. Mas o tempo que eu teria gasto pensando e dando algumas facadas no código é gasto escrevendo testes. E é menos tempo. Para mim. Não para todos. E, mesmo que demorasse a mesma quantidade de tempo, isso me deixaria com um conjunto de testes, para que a refatoração fosse mais segura, levando a um código ainda melhor na linha.
Como isso acontece?
Primeiro, ele me incentiva a pensar em todas as classes como um serviço para algum código de cliente. Um código melhor vem do pensamento sobre como o código de chamada deseja usar a API, em vez de se preocupar com a aparência do código.
Segundo, me impede de escrever muita complexidade ciclomética em um método, enquanto estou pensando nisso. Cada caminho extra através de um método tende a dobrar o número de testes que preciso fazer. A preguiça pura determina que, depois de adicionar muita lógica, e eu precisar escrever 16 testes para adicionar uma condição, é hora de extrair parte dela para outro método / classe e testá-la separadamente.
É realmente assim tão simples. Não é uma ferramenta de design mágico.
fonte
Esses requisitos devem ser reafirmados em termos humanos. Quem quer saber quais produtos o usuário visualizou anteriormente? O usuário? Um vendedor?
Quão? Pelo nome? Por marca? A primeira etapa no desenvolvimento orientado a teste é definir um teste, por exemplo:
>
Se esses são os únicos requisitos, eu certamente não saltaria para criar um ProductService. Eu poderia criar uma página da web muito simples com uma lista de produtos estática. Isso funcionaria perfeitamente até você atingir os requisitos para adicionar e excluir produtos. Nesse ponto, posso decidir que é mais simples usar um banco de dados relacional e um ORM e criar uma classe de produto mapeada para uma única tabela. Ainda não há ProductService. Classes como ProductService serão criadas quando e se forem necessárias. Pode haver várias solicitações da Web que precisam executar as mesmas consultas ou atualizações. Em seguida, a classe ProductService será criada para impedir a duplicação de código.
Em resumo, o TDD direciona o código a ser gravado. O design acontece quando você faz escolhas de implementação e depois refatora o código em classes para eliminar a duplicação e controlar as dependências. Ao adicionar código, você precisará criar novas classes para manter o código SOLID. Mas você não precisa decidir antecipadamente que precisará de uma classe Product e uma classe ProductService. Você pode achar que a vida é perfeita com apenas uma classe de produto.
fonte
ProductService
então. Mas como o TDD disse que você precisava de um banco de dados e um ORM?Outros podem discordar, mas para mim muitas das metodologias mais recentes se baseiam na suposição de que o desenvolvedor fará a maior parte do que as metodologias mais antigas explicaram apenas por hábito ou orgulho pessoal, que o desenvolvedor geralmente está fazendo algo que é bastante óbvio para eles, e o trabalho é encapsulado em uma linguagem limpa ou nas partes mais limpas de uma linguagem um tanto bagunçada, para que você possa fazer todos os negócios de teste.
Alguns exemplos em que me deparei com isso no passado:
Pegue um grupo de contratados especializados e diga a eles que a equipe deles é Agile e Test First. Eles geralmente não têm outro hábito além de trabalhar com especificações e não se preocupam com a qualidade do trabalho, desde que ele dure o tempo suficiente para concluir o projeto.
Tente fazer algo novo primeiro, gaste muito do seu tempo testando testes, pois você acha várias abordagens e interfaces uma porcaria.
Codifique algo de baixo nível e receba um tapa por falta de cobertura ou escreva muitos testes que não valem muito valor, porque você não pode zombar dos comportamentos subjacentes aos quais está vinculado.
Se você está fazendo TDD e está funcionando para você, é bom para você, mas existem muitas coisas (trabalhos inteiros ou estágios de um projeto) por aí onde isso simplesmente não agrega valor.
Seu exemplo parece que você ainda não fez parte de um design; portanto, você precisa ter uma conversa sobre arquitetura ou está fazendo prototipagem. Você precisa passar por isso primeiro em minha opinião.
fonte
Estou convencido de que o TDD é uma abordagem muito valiosa para o design detalhado do sistema - ou seja, as APIs e o modelo de objeto. No entanto, para chegar ao ponto em um projeto em que você começaria a usar o TDD, é necessário que o quadro geral do design já tenha sido modelado de alguma maneira e o quadro geral da arquitetura já modelado de algum modo. @ user414076 parafraseia Robert Martin como tendo uma ideia de design em mente, mas não sendo casada com ela. Exatamente. Conclusão - TDD não é a única atividade de design em andamento, é como os detalhes do design são aprimorados. O TDD deve ser precedido por outras atividades de design e se encaixar em uma abordagem geral (como o Agile), que aborda como o design geral é criado e evoluído.
FYI - dois livros que recomendo sobre o tópico que dão exemplos tangíveis e realistas:
Crescimento de software orientado a objetos, guiado por testes - explica e fornece um exemplo completo do projeto. Este é um livro sobre design, não teste . O teste é usado como um meio de especificar o comportamento esperado durante as atividades de design.
desenvolvimento orientado a testes Um Guia Prático - uma caminhada lenta e passo a passo no desenvolvimento de um aplicativo completo, embora pequeno.
fonte
O TTD impulsiona a descoberta do projeto por falha no teste, não por sucesso. Portanto, você pode testar as incógnitas e retestar iterativamente à medida que as incógnitas são expostas, o que leva a um conjunto completo de testes de unidade - uma coisa muito boa para manutenção contínua e uma coisa muito difícil de tentar. atualização após o código ser gravado / liberado.
Por exemplo, um requisito pode ser que a entrada possa estar em vários formatos diferentes, nem todos são conhecidos ainda. Usando o TDD, você primeiro escreveria um teste que verifica se a saída apropriada é fornecida, em qualquer formato de entrada. Obviamente, esse teste falhará, então você escreve um código para lidar com os formatos conhecidos e realiza um novo teste. Como os formatos desconhecidos são expostos através da coleta de requisitos, novos testes são gravados antes que o código seja gravado, e eles também devem falhar. Em seguida, um novo código é gravado para dar suporte aos novos formatos e todos os testes são executados novamente, reduzindo a chance de regressão.
Também é útil pensar na falha da unidade como código "inacabado" em vez de código "quebrado". O TDD permite unidades inacabadas (falhas esperadas), mas reduz a ocorrência de unidades quebradas (falhas inesperadas).
fonte
Na pergunta está afirmado:
Eles chegaram a essa conclusão pensando em como iam testar este produto. "Que tipo de produto faz isso?" "Bem, nós poderíamos criar um serviço". "Ok, vamos escrever um teste para esse serviço"
fonte
Uma funcionalidade pode ter muitos designs e o TDD não indica completamente qual é o melhor. Mesmo que os testes o ajudem a criar um código mais modular, ele também pode levar a criar módulos que atendam aos requisitos de testes e não à realidade da produção. Então você precisa entender para onde está indo e como as coisas devem se encaixar em todo o cenário. Em outras palavras, existem requisitos funcionais e não funcionais, não se esqueça do último.
Em relação ao design, refiro-me aos livros de Robert C. Martin (Desenvolvimento Ágil), mas também aos Padrões de Arquitetura de Aplicativos Corporativos e Design de Driver de Domínio de Martin Fowler. O último, especialmente, é muito sistemático ao extrair as Entidades e Relações dos requisitos.
Então, quando você tiver uma boa noção das opções disponíveis sobre como gerenciar essas entidades, poderá alimentar sua abordagem de TDD.
fonte
Não.
Como você pode testar algo que você não criou primeiro?
Estes não são requisitos, são definições de dados. Não sei qual é o negócio do seu software, mas não é provável que os analistas falem dessa maneira.
Você precisa saber quais são os invariantes do seu sistema.
Um requisito seria algo como:
Portanto, se esse é o único requisito, você pode ter uma classe como:
Em seguida, usando TDD, você escreveria um caso de teste antes de implementar o método order ().
Portanto, o segundo teste falhará, e você poderá implementar o método order () da maneira que desejar.
fonte
Você está certo de que o TDD resultará em uma boa implementação de um determinado design. Não ajudará no seu processo de design.
fonte
O TDD ajuda muito, no entanto, há parte importante no desenvolvimento de software. O desenvolvedor deve ouvir o código que está sendo gravado. A refatoração é a 3ª parte do ciclo TDD. Este é o passo principal em que o desenvolvedor deve se concentrar e pensar antes de ir para o próximo teste vermelho. Existe alguma duplicação? Os princípios do SOLID são aplicados? E quanto à alta coesão e baixo acoplamento? E os nomes? Dê uma olhada no código que está emergindo dos testes e veja se há algo que precisa ser alterado, redesenhado. Pergunta o código e o código dirá a você como ele deseja que seja projetado. Geralmente escrevo conjuntos de vários testes, examino essa lista e crio o primeiro design simples, não precisa ser "final", geralmente não é, porque mudou ao adicionar novos testes. É aí que o design vem.
fonte