O que eu perco ao adotar o design orientado a testes?
Listar apenas negativos; não liste os benefícios gravados de forma negativa.
O que eu perco ao adotar o design orientado a testes?
Listar apenas negativos; não liste os benefícios gravados de forma negativa.
Respostas:
Várias desvantagens (e não estou afirmando que não há benefícios - especialmente ao escrever a base de um projeto - economizaria muito tempo no final):
Se você deseja fazer o TDD "real" (leia: teste primeiro com as etapas de vermelho, verde e refatorar), também é necessário começar a usar zombarias / stubs, quando quiser testar os pontos de integração.
Ao começar a usar zombarias, depois de um tempo, você desejará começar a usar Injeção de Dependência (DI) e um contêiner de Inversão de Controle (IoC). Para fazer isso, você precisa usar interfaces para tudo (que têm muitas armadilhas).
No final do dia, você precisa escrever muito mais código do que se você apenas fizer isso da "maneira simples e antiga". Em vez de apenas uma classe de cliente, você também precisa escrever uma interface, uma classe simulada, algumas configurações de IoC e alguns testes.
E lembre-se de que o código de teste também deve ser mantido e tratado. Os testes devem ser tão legíveis quanto todo o resto e leva tempo para escrever um bom código.
Muitos desenvolvedores não entendem como fazer tudo isso "do jeito certo". Mas como todo mundo diz a eles que o TDD é a única maneira verdadeira de desenvolver software, eles apenas tentam o melhor que podem.
É muito mais difícil do que se possa pensar. Muitas vezes, os projetos feitos com TDD acabam com muito código que ninguém realmente entende. Os testes de unidade geralmente testam a coisa errada, da maneira errada. E ninguém concorda com a aparência de um bom teste, nem mesmo os chamados gurus.
Todos esses testes tornam muito mais difícil "alterar" (ao contrário da refatoração) o comportamento do seu sistema e alterações simples tornam-se muito difíceis e demoradas.
Se você lê a literatura do TDD, sempre existem alguns exemplos muito bons, mas geralmente em aplicativos da vida real, você deve ter uma interface de usuário e um banco de dados. É aqui que o TDD fica realmente difícil, e a maioria das fontes não oferece boas respostas. E se o fazem, sempre envolve mais abstrações: objetos simulados, programação para uma interface, padrões MVC / MVP etc., que novamente exigem muito conhecimento e ... você precisa escrever ainda mais código.
Portanto, tenha cuidado ... se você não possui uma equipe entusiasta e pelo menos um desenvolvedor experiente que sabe escrever bons testes e também sabe algumas coisas sobre uma boa arquitetura, é preciso pensar duas vezes antes de seguir o caminho do TDD .
Quando você chega ao ponto em que possui um grande número de testes, alterar o sistema pode exigir a reescrita de alguns ou de todos os seus testes, dependendo de quais foram invalidados pelas alterações. Isso pode transformar uma modificação relativamente rápida em uma que consome muito tempo.
Além disso, você pode começar a tomar decisões de design com base mais em TDD do que em bons princípios de design. Enquanto você pode ter uma solução muito simples e fácil, impossível de testar da maneira que o TDD exige, agora você tem um sistema muito mais complexo que é mais propenso a erros.
if part of the system is covered by tests and they pass, then everything is fine (including design)
.
Eu acho que o maior problema para mim é a enorme perda de tempo que leva para "entrar nele". Ainda estou muito no início da minha jornada com o TDD (veja meu blog para atualizações, minhas aventuras de teste, se você estiver interessado) e eu literalmente passei horas começando.
Leva muito tempo para colocar seu cérebro no "modo de teste" e escrever "código testável" é uma habilidade em si.
TBH, eu discordo respeitosamente dos comentários de Jason Cohen sobre tornar públicos os métodos privados, não é disso que se trata. Não criei mais métodos públicos em minha nova maneira de trabalhar do que antes . No entanto, envolve mudanças na arquitetura e permite que você "ligue" módulos de código para tornar tudo mais fácil de testar. Você não deve tornar os internos do seu código mais acessíveis para fazer isso. Caso contrário, estamos de volta à estaca zero, com tudo sendo público, onde está o encapsulamento?
Então, (IMO) em poucas palavras:
PS: Se você gostaria de links para positivos, eu perguntei e respondi várias perguntas sobre ele, confira meu perfil .
Nos poucos anos em que pratico o Test Driven Development, devo dizer que as maiores desvantagens são:
TDD é melhor feito em pares. Por um lado, é difícil resistir ao desejo de apenas escrever a implementação quando você sabe como escrever uma declaração if / else . Mas um par o manterá na tarefa porque você o manterá na tarefa. Infelizmente, muitas empresas / gerentes não pensam que este seja um bom uso de recursos. Por que pagar duas pessoas para escrever um recurso, quando eu tenho dois recursos que precisam ser executados ao mesmo tempo?
Algumas pessoas simplesmente não têm paciência para escrever testes de unidade. Alguns têm muito orgulho do seu trabalho. Ou, alguns gostam de ver métodos / funções complicados sangrar no final da tela. TDD não é para todos, mas eu realmente gostaria que fosse. Isso tornaria a manutenção de coisas muito mais fácil para aquelas pobres almas que herdam código.
Idealmente, seus testes só serão interrompidos quando você tomar uma decisão incorreta de código. Ou seja, você pensou que o sistema funcionava de uma maneira, e acontece que não. Ao quebrar um teste ou um (pequeno) conjunto de testes, essas são realmente boas notícias. Você sabe exatamente como o seu novo código afetará o sistema. No entanto, se seus testes são mal escritos, fortemente acoplados ou, pior ainda, gerados ( tosse VS Test), a manutenção de seus testes pode se tornar um coro rapidamente. E, depois que testes suficientes começarem a causar mais trabalho do que o valor percebido que eles estão criando, os testes serão a primeira coisa a ser excluída quando os agendamentos forem compactados (por exemplo, chega ao tempo de processamento)
Idealmente, novamente, se você seguir a metodologia, seu código será 100% testado por padrão. Normalmente, pensei, acabo com uma cobertura de código acima de 90%. Isso geralmente acontece quando tenho alguma arquitetura de estilo de modelo e a base é testada, e tento cortar os cantos e não testar as personalizações do modelo. Além disso, descobri que, quando encontro uma nova barreira que não havia encontrado anteriormente, tenho uma curva de aprendizado em testá-la. Eu vou admitir que escrevi algumas linhas de código da maneira antiga do skool, mas eu realmente gosto de ter esses 100%. (Eu acho que eu era um realizador na escola, er skool).
No entanto, com isso, eu diria que os benefícios do TDD superam em muito os negativos da idéia simples de que, se você puder realizar um bom conjunto de testes que cubram sua aplicação, mas não sejam tão frágeis que uma alteração os interrompa, você poderá continuar adicionando novos recursos no dia 300 do seu projeto, como você fez no dia 1. Isso não acontece com todos aqueles que experimentam o TDD pensando que é uma bala mágica para todo o código deles, e assim eles acham que pode não funciona, ponto final.
Pessoalmente, descobri que, com o TDD, escrevo código mais simples, passo menos tempo debatendo se uma solução de código específica funcionará ou não, e que não tenho medo de alterar qualquer linha de código que não atenda aos critérios estabelecidos por O time.
O TDD é uma disciplina difícil de dominar, e eu pratico isso há alguns anos, e ainda aprendo novas técnicas de teste o tempo todo. É um investimento enorme de tempo, mas, a longo prazo, sua sustentabilidade será muito maior do que se você não tivesse testes de unidade automatizados. Agora, se meus chefes pudessem descobrir isso.
No seu primeiro projeto de TDD, há duas grandes perdas, tempo e liberdade pessoal
Você perde tempo porque:
Você perde a liberdade pessoal porque:
Espero que isto ajude
O TDD exige que você planeje como suas classes funcionarão antes de escrever o código para passar nesses testes. Isso é um mais e um menos.
Acho difícil escrever testes no "vácuo" - antes que qualquer código tenha sido escrito. Na minha experiência, tenho tendência a tropeçar nos meus testes sempre que penso inevitavelmente em alguma coisa enquanto escrevia minhas aulas que esqueci enquanto escrevia meus testes iniciais. Então é hora de não apenas refatorar minhas aulas, mas também meus testes. Repita isso três ou quatro vezes e pode ser frustrante.
Prefiro escrever um rascunho de minhas aulas primeiro e depois escrever (e manter) uma bateria de testes de unidade. Depois que eu tenho um rascunho, o TDD funciona bem para mim. Por exemplo, se um bug for relatado, escreverei um teste para explorar esse bug e, em seguida, corrigirei o código para que o teste seja aprovado.
A prototipagem pode ser muito difícil com o TDD - quando você não tem certeza de qual caminho seguirá para uma solução, escrever os testes antecipadamente pode ser difícil (além dos muito amplos). Isso pode ser uma dor.
Honestamente, eu não acho que, para o "desenvolvimento central" da grande maioria dos projetos, haja alguma desvantagem real; é muito mais falado do que deveria ser, geralmente por pessoas que acreditam que seu código é bom o suficiente para que não precisem de testes (nunca é) e pessoas que simplesmente não podem se dar ao trabalho de escrevê-las.
Bem, e esse alongamento, você precisa depurar seus testes. Além disso, há um certo custo de tempo para escrever os testes, embora muitas pessoas concordem que é um investimento inicial que compensa durante a vida útil do aplicativo, tanto na depuração economizada quanto na estabilidade.
O maior problema que eu pessoalmente tive com isso, porém, é adquirir a disciplina para realmente escrever os testes. Em uma equipe, especialmente uma equipe estabelecida, pode ser difícil convencê-los de que o tempo gasto vale a pena.
Se seus testes não forem muito completos, você pode cair no falso senso de "tudo funciona" apenas porque você passa nos testes. Teoricamente, se seus testes forem aprovados, o código está funcionando; mas se pudéssemos escrever código perfeitamente na primeira vez, não precisaríamos de testes. A moral aqui é certificar-se de fazer uma verificação de sanidade por conta própria antes de chamar algo completo, não confie apenas nos testes.
Nessa nota, se a sua verificação de integridade encontrar algo que não foi testado, volte e escreva um teste.
A desvantagem do TDD é que ele geralmente está fortemente associado à metodologia 'Agile', que não dá importância à documentação de um sistema, e sim ao entendimento por que um teste 'deve' retornar um valor específico, em vez de qualquer outro, reside apenas no desenvolvedor cabeça.
Assim que o desenvolvedor deixa ou esquece o motivo pelo qual o teste retorna um valor específico e não outro, você está ferrado. O TDD é bom se estiver adequadamente documentado e cercado por documentação legível por humanos (por exemplo, gerente de cabelos pontudos) que pode ser referida em 5 anos quando o mundo muda e o aplicativo também precisa.
Quando falo em documentação, isso não é um problema de código, é uma escrita oficial que existe fora do aplicativo, como casos de uso e informações básicas que podem ser consultadas por gerentes, advogados e pelo pobre sapo que precisa atualizar seu código em 2011.
Eu encontrei várias situações em que o TDD me deixa louco. Para citar alguns:
Manutenção de caso de teste:
Se você está em uma grande empresa, muitas chances são de que você não precisa escrever os casos de teste sozinho ou pelo menos a maioria deles é escrita por outra pessoa quando você entra na empresa. Os recursos de um aplicativo mudam de tempos em tempos e, se você não tiver um sistema, como o HP Quality Center, para rastreá-los, você ficará louco rapidamente.
Isso também significa que os novos membros da equipe levarão um bom tempo para entender o que está acontecendo com os casos de teste. Por sua vez, isso pode ser traduzido em mais dinheiro necessário.
Complexidade de automação de teste:
Se você automatizar alguns ou todos os casos de teste em scripts de teste executáveis em máquina, precisará garantir que esses scripts de teste estejam sincronizados com os casos de teste manuais correspondentes e alinhados com as alterações do aplicativo.
Além disso, você gastará tempo para depurar os códigos que ajudam a detectar bugs. Na minha opinião, a maioria desses erros vem da falha da equipe de teste em refletir as alterações do aplicativo no script de teste de automação. Alterações na lógica de negócios, GUI e outras coisas internas podem fazer com que seus scripts parem de ser executados ou não sejam confiáveis. Às vezes, as mudanças são muito sutis e difíceis de detectar. Uma vez que todos os meus scripts relatam falhas porque basearam seus cálculos nas informações da tabela 1, enquanto a tabela 1 agora era a tabela 2 (porque alguém trocou o nome dos objetos da tabela no código do aplicativo).
O maior problema são as pessoas que não sabem escrever testes de unidade adequados. Eles escrevem testes que dependem um do outro (e funcionam muito bem com o Ant, mas, de repente, falham quando eu os executo no Eclipse, apenas porque eles são executados em ordem diferente). Eles escrevem testes que não testam nada em particular - eles apenas depuram o código, verificam o resultado e o transformam em teste, chamando-o de "test1". Eles ampliam o escopo de classes e métodos, apenas porque será mais fácil escrever testes de unidade para eles. O código dos testes de unidade é terrível, com todos os problemas clássicos de programação (acoplamento pesado, métodos com 500 linhas de comprimento, valores codificados, duplicação de código) e é um inferno para manter. Por alguma estranha razão, as pessoas tratam os testes de unidade como algo inferior ao código "real" e não não se preocupam com a qualidade deles. :-(
Você perde muito tempo gasto escrevendo testes. Obviamente, isso pode ser salvo no final do projeto, capturando erros mais rapidamente.
A maior desvantagem é que, se você realmente deseja fazer o TDD corretamente, terá que falhar muito antes de ter sucesso. Dado o número de empresas de software que trabalham (dólar por KLOC), você acabará sendo demitido. Mesmo que seu código seja mais rápido, mais limpo, mais fácil de manter e tenha menos erros.
Se você estiver trabalhando em uma empresa que paga pelos KLOCs (ou requisitos implementados - mesmo que não tenham sido testados), fique longe do TDD (ou revisões de código, ou programação de pares, ou Integração Contínua, etc. etc. etc.).
Você perde a capacidade de dizer que está "pronto" antes de testar todo o seu código.
Você perde a capacidade de escrever centenas ou milhares de linhas de código antes de executá-lo.
Você perde a oportunidade de aprender através da depuração.
Você perde a flexibilidade de enviar código do qual não tem certeza.
Você perde a liberdade de acoplar firmemente seus módulos.
Você perde a opção de ignorar a documentação de design de baixo nível.
Você perde a estabilidade que acompanha o código que todo mundo tem medo de alterar.
Segundo a resposta sobre o tempo de desenvolvimento inicial. Você também perde a capacidade de trabalhar confortavelmente sem a segurança dos testes. Também fui descrito como um nutbar TDD, para que você possa perder alguns amigos;)
É percebido como mais lento. A longo prazo, isso não é verdade em termos de tristeza, pois isso o ajudará a economizar no futuro, mas você acabará escrevendo mais código, de modo que, sem dúvida, estará gastando tempo em "testar não codificar". É um argumento imperfeito, mas você perguntou!
Reorientar-se para requisitos difíceis e imprevistos é o constante banimento do programador. O desenvolvimento orientado a testes obriga você a se concentrar nos requisitos mundanos já conhecidos e limita seu desenvolvimento ao que já foi imaginado.
Pense bem: é provável que você acabe projetando casos de teste específicos, para que não seja criativo e comece a pensar "seria legal se o usuário pudesse fazer X, Y e Z". Portanto, quando esse usuário começa a ficar entusiasmado com os possíveis requisitos legais X, Y e Z, seu design pode ser muito rígido em casos de teste já especificados, e será difícil ajustá-lo.
Esta, é claro, é uma faca de dois gumes. Se você gastar todo o seu tempo projetando para todos os X, Y e Z concebíveis, imagináveis que um usuário possa desejar, inevitavelmente nunca concluirá nada. Se você concluir algo, será impossível que qualquer pessoa (inclusive você) tenha alguma idéia do que está fazendo no seu código / design.
Pode ser demorado e demorado escrever testes para dados "aleatórios", como feeds XML e bancos de dados (não tão difíceis). Ultimamente, passo algum tempo trabalhando com feeds de dados climáticos. É muito confuso escrever testes para isso, pelo menos porque eu não tenho muita experiência com TDD.
Você perderá grandes classes com múltiplas responsabilidades. Você provavelmente também perderá métodos grandes com várias responsabilidades. Você pode perder alguma capacidade de refatorar, mas também perderá parte da necessidade de refatorar.
Jason Cohen disse algo como: TDD requer uma certa organização para o seu código. Isso pode estar errado na arquitetura; por exemplo, como os métodos privados não podem ser chamados fora de uma classe, você deve tornar os métodos não privados para torná-los testáveis.
Eu digo que isso indica uma abstração perdida - se o código privado realmente precisa ser testado, provavelmente deve estar em uma classe separada.
Dave Mann
Você precisa escrever aplicativos de uma maneira diferente: uma que os torne testáveis. Você ficaria surpreso com o quão difícil isso é a princípio.
Algumas pessoas acham o conceito de pensar no que vão escrever antes de escreverem muito. Conceitos como zombaria também podem ser difíceis para alguns. O TDD em aplicativos herdados pode ser muito difícil se não foram projetados para teste. TDD em torno de estruturas que não são compatíveis com TDD também pode ser uma luta.
O TDD é uma habilidade para que os desenvolvedores juniores possam ter dificuldades no início (principalmente porque não foram ensinados a trabalhar dessa maneira).
No geral, embora os contras se resolvam à medida que as pessoas se tornam hábeis e você acaba abstraindo o código 'fedorento' e tendo um sistema mais estável.
Boas respostas a todos. Eu adicionaria algumas maneiras de evitar o lado sombrio do TDD:
Eu escrevi aplicativos para fazer seu próprio autoteste aleatório. O problema de escrever testes específicos é que, mesmo que você escreva muitos deles, eles cobrem apenas os casos em que você pensa. Os geradores de teste aleatório encontram problemas nos quais você não pensou.
Todo o conceito de muitos testes de unidade implica que você possui componentes que podem entrar em estados inválidos, como estruturas de dados complexas. Se você ficar longe de estruturas de dados complexas, há muito menos a ser testado.
Na medida em que seu aplicativo o permitir, seja tímido quanto ao design, que depende da ordem adequada de notificações, eventos e efeitos colaterais. Elas podem ser facilmente descartadas ou embaralhadas e, portanto, precisam de muitos testes.
O TDD requer uma certa organização para o seu código. Isso pode ser ineficiente ou difícil de ler. Ou mesmo arquitetonicamente errado; por exemplo, como os private
métodos não podem ser chamados fora de uma classe, você deve tornar os métodos não privados para torná-los testáveis, o que está errado.
Quando o código muda, você também precisa alterar os testes. Com a refatoração, isso pode dar muito trabalho extra.
Deixe-me acrescentar que, se você aplicar os princípios do BDD a um projeto de TDD, poderá aliviar algumas das principais desvantagens listadas aqui (confusão, mal-entendidos etc.). Se você não conhece o BDD, leia a introdução de Dan North. Ele criou o conceito em resposta a alguns dos problemas que surgiram da aplicação do TDD no local de trabalho. A introdução de Dan ao BDD pode ser encontrada aqui .
Só faço essa sugestão porque o BDD aborda alguns desses pontos negativos e atua como uma brecha. Você deve considerar isso ao coletar seus comentários.
Você precisa garantir que seus testes estejam sempre atualizados, no momento em que você começa a ignorar as luzes vermelhas é o momento em que os testes se tornam sem sentido.
Você também precisa garantir que os testes sejam abrangentes ou, no momento em que um grande bug aparecer, o tipo de gerenciamento abafado que você finalmente convenceu a deixar gastar tempo escrevendo mais código irá reclamar.
A pessoa que ensinou o desenvolvimento ágil da minha equipe não acreditava no planejamento, você apenas escreveu o mínimo possível.
Seu lema era refatorar, refatorar, refatorar. Cheguei a entender que refatorar significava "não planejar com antecedência".
O tempo de desenvolvimento aumenta: todo método precisa de teste e, se você tiver um aplicativo grande com dependências, precisará preparar e limpar seus dados para testes.