Devo escrever testes quando posso provar a correção do código?

8

As pessoas dizem que "falar sobre TDD dificilmente funciona, se você quiser convencer alguém a TDD, mostre-lhes resultados". No entanto, já estou obtendo ótimos resultados sem o TDD. Mostrando que as pessoas que usam TDD obtêm bons resultados não serão convincentes, quero ver que as pessoas que escrevem TDD e não TDD obtêm melhores resultados com TDD.

Apesar de tudo isso, estou interessado em experimentar o TDD. No entanto, não estou convencido de que vou ganhar algo com isso. Se isso for útil, tentarei empurrá-lo para o resto da minha equipe.

Minha principal pergunta é a seguinte: o TDD serviria a algum propósito de código, se eu já puder provar a correção do código?

Obviamente, nenhum deles é uma bala de prata. Sua prova pode estar errada porque você perdeu um detalhe e seu teste pode falhar em detectar um bug que você não testou. No final, somos humanos, ninguém pode criar um código 100% livre de bugs para sempre. Só podemos nos esforçar para chegar o mais perto possível.

No entanto, o TDD realmente economizaria algum tempo em código que tivesse sua correção comprovada? código que, na máquina de estados em que o código opera, todos os possíveis estados válidos e seus intervalos são reconhecidos pelo desenvolvedor, todos são contabilizados e o código é projetado em uma verificação de erro no estilo de lista de permissões que passa todas as exceções para um manipulador superior para garantir que nada de vazamento inesperado -> sem exibir uma mensagem relevante (dentro do motivo) para o cliente e enviar notificações de log para um administrador.

Respostas com exemplos da vida real seriam melhores.


Alguns esclarecimentos:

  • Esta questão não é sobre se você pode provar a correção do código ou não. Vamos supor, por padrão, que nem todo código possa ser provado correto dentro de um prazo razoável, mas que algumas partes do código podem ser. Por exemplo, é muito fácil comprovar a correção de um módulo FizzBuzz. Não é muito fácil para um serviço de sincronização de dados baseado em nuvem.

  • Dentro desse limite, a pergunta é a seguinte: Comece com a suposição de que uma base de código é dividida em 2 partes: [I] partes que foram provadas corretas [II] partes que não foram provadas corretas, mas testadas manualmente para funcionar.

  • Quero aplicar as práticas de TDD a essa base de código que não as possuía até agora. A pergunta é a seguinte: o TDD deve ser aplicado a todos os módulos, ou seria suficiente aplicá-los apenas aos módulos que não se mostraram corretos?

  • "Comprovado correto" significa que você pode considerar este módulo completamente funcional, ou seja, ele não depende de nenhum estado global ou externo fora de si mesmo, e possui sua própria API de E / S que outros módulos que interagem com ele devem seguir . Não é possível "quebrar este módulo" alterando o código fora do módulo; na pior das hipóteses, você pode usá-lo incorretamente e receber mensagens de erro formatadas.

  • Obviamente, todas as regras têm exceções; os erros do compilador nas novas versões do compilador podem introduzir erros neste módulo, mas os mesmos erros podem ser introduzidos nos testes que a testaram e resultam em uma falsa sensação de segurança nos testes que não funcionam mais como planejado. A conclusão é que os testes não são uma solução mágica, eles são outra camada de proteção, e esta pergunta discute a questão de saber se essa camada de proteção vale o esforço no caso específico de um módulo que foi provado correto (suponha que foi de fato).

Kylee
fonte
10
Provar "correção do código" pode se tornar mais difícil do que você pensa.
πάντα ῥεῖ
34
Embora eu tenha gostado de aprender sobre seu local de trabalho e seu histórico de trabalho, quase parei de ler várias vezes porque, às vezes, o que é realmente importante é que, como forma de maximizar seus resultados, e para manter a atenção de seus leitores para que eles possam, em um esforço para fortalecer seu conhecimento e o da comunidade, ajudá-lo, você deve ... chegar ao ponto . Por favor, considere encurtar sua pergunta para os pontos mais salientes.
MetaFight 2/04
10
O que você descreve não é um processo de "comprovação de correção" do código. Esse tipo de processo é drasticamente diferente. Além disso, acho difícil aceitar que você possa criar código de uma maneira que possa ser "provada correta" apenas olhando para ele. Vi muitos códigos que pareciam triviais e "corretos", apenas para serem totalmente esmagados quando submetidos a testes automatizados robustos.
eufórico
8
"Cuidado com os bugs no código acima; eu apenas provei que está correto, não tentei." -Donald Knuth.
214 Neil
6
Sua pergunta não faz sentido. TDD significa que os testes conduzem o desenvolvimento. Em outras palavras, você não tem design, arquitetura ou código, a menos que tenha um teste para isso. Então, como você está "aplicando TDD ao código que foi provado correto" quando, pela própria definição de TDD, não há código para provar que está correto ?
Jörg W Mittag

Respostas:

20

Sim.

As provas são boas quando estão disponíveis, mas mesmo na melhor das hipóteses, elas apenas provam que um único bit de código funcionará conforme o esperado (para todas as entradas - responsável por interrupções no meio de qualquer operação - e quanto à falta de memória ? falha no disco? falha na rede?).

O que acontece quando muda?

Os testes são ótimos porque servem como um contrato implícito sobre o que o código deve fazer. Eles fornecem alguns andaimes para que seu novo estagiário possa entrar e fazer alterações com algum nível de confiança. Tudo através de resultados rápidos e claros: passa ou falha.

E, francamente, posso treinar um estagiário para escrever testes de unidade viáveis ​​em alguns meses. Duvido que alguém da minha equipe (inclusive eu) possa criar provas que garantam algo significativo para código não trivial; e muito menos fazê-lo com rapidez e precisão.

Telastyn
fonte
O mesmo código que não seria trivial para provar também não seria trivial para testar. Uma falha de rede ou de disco pode assumir várias formas. Como você pode ter certeza de que seus testes cobriram todos os cenários possíveis? Eles provavelmente cobrirão todos os cenários em que você poderia pensar, mas não necessariamente todos os cenários da vida real. Você não pode simplesmente assumir cegamente que todas as alterações serão ininterruptas apenas porque você possui testes. É apenas mais uma camada de proteção. Os testes são importantes, mas não são uma bala de prata.
Kylee
6
@ Kylee - sem ofensas, mas você parece estar subestimando o esforço e a habilidade necessários para fazer uma prova de correção (ou superestimando o esforço e a habilidade necessários para lançar alguns testes automatizados).
Telastyn #
Talvez você esteja certo, e eu provavelmente superestimo o esforço de montar testes devido à inexperiência com ele (honestamente, é menos o "montar testes" que me preocupa e mais sobre realmente executar os testes. Para projetos grandes e que mudam rapidamente, isso por si só não é um desperdício de tempo insignificante). De qualquer forma, isso não tem nada a ver com a questão. A pergunta afirma claramente, suponha que você tenha um código que já foi comprovado correto, ainda há um ponto para escrever testes de unidade para ele?
Kylee
1
For large & rapidly changing projectsquanto mais propensos a mudar, mais necessários são os testes, pois um código alterado tem muito mais chances de falhar devido a novos erros ou comportamentos inesperados do que o código que mal muda. É uma questão de probabilidade. Mesmo que não mude com frequência, após algum tempo o conhecimento obtido durante o desenvolvimento pode se perder ou cair no esquecimento. Os testes também são conhecimentos materializados, que podem reduzir significativamente a curva de aprendizado. Os testes de codificação são demorados? Sim. Isso torna o projeto mais caro? Não, a longo prazo torne mais barato .
LAIV
3
@ Kylee Mesmo se você puder provar que um código está correto, na melhor das hipóteses você estaria na situação em que ninguém tem permissão para alterar esse código ou adicionar recursos a ele; fazer isso invalidaria sua prova. Como já destacado nesta resposta, os testes permitem alterar seu código com confiança. Mesmo que a pessoa que está mudando seja um estagiário inexperiente. A propósito, mesmo que algum bloco de código esteja 100% correto, isso não significa que você nunca precisará alterá-lo (por exemplo, para adicionar um novo recurso necessário ao cliente ou para satisfazer alguma restrição do mundo real que não é considerada em sua prova).
Brandin
5

Nós não sabemos. Não podemos responder à sua pergunta.

Enquanto você gasta muito tempo explicando esse processo que agora parece funcionar para a satisfação de todos, você está nos dizendo apenas uma pequena fatia do que realmente está acontecendo.

Pela minha experiência, o que você está descrevendo é extrema raridade e eu sou cético quanto ao fato de que o processo e a abordagem de codificação são realmente causa de baixa contagem de bugs em seus aplicativos. Pode haver muitos outros fatores que influenciam seus aplicativos e você não está nos dizendo nada sobre esses fatores.

Portanto, não sabemos, apesar de não conhecermos exatamente o seu ambiente e cultura de desenvolvimento, se o TDD o ajudará ou não. E podemos passar dias discutindo e discutindo sobre isso.

Há apenas uma recomendação que podemos oferecer: experimente. Experimentar. Aprenda. Eu sei que você está tentando gastar menos esforço para decidir, mas isso não é possível. Se você realmente quer saber se o TDD funcionará no seu contexto, a única maneira de descobrir é realmente fazer o TDD. Se você realmente aprender e aplicá-lo ao seu aplicativo, poderá compará-lo com o seu processo não TDD. Pode ser que o TDD realmente tenha vantagens e você decida mantê-lo. Ou então, o TDD não traz nada de novo e apenas o atrasa. Nesse caso, você pode voltar ao processo anterior.

Eufórico
fonte
5

O principal objetivo dos testes (unitários) é proteger o código, garantindo que ele não seja despercebido devido a alterações posteriores. Quando o código é escrito pela primeira vez, ele recebe muita atenção e é examinado. E você pode ter algum sistema superior para isso.

Seis meses depois, quando alguém estiver trabalhando em algo aparentemente não relacionado, ele poderá se romper e o seu superprovedor de correção de código não notará. Um teste automático será.

Martin Maat
fonte
1
Isso é algo que é muitas vezes esquecido. Sim, há algumas dificuldades para garantir que o código tenha testes de unidade agora. Mas isso se pagará muitas vezes quando um desenvolvedor júnior fizer uma alteração simples e os testes de unidade notificarem imediatamente que eles quebraram outra coisa. Fui o desenvolvedor júnior décadas atrás, que quebrou algo e todo o lançamento foi adiado. Embora meus colegas fossem muito tolerantes na época e estivessem de costas quando os gerentes começaram a pular para cima e para baixo, todo o cenário poderia, em retrospectiva, ter sido evitado se os testes de unidade estivessem em vigor.
Robbie Dee
5

Quero aplicar as práticas de TDD a essa base de código que não as possuía até agora.

Esta é a maneira mais difícil de aprender TDD. Quanto mais tarde você testar, mais custará escrever testes e menos você os escreverá.

Não estou dizendo que é impossível adaptar os testes a uma base de código existente. Estou dizendo que isso provavelmente não transformará ninguém em um crente no TDD. Isso é trabalho duro.

Na verdade, é melhor praticar TDD pela primeira vez em algo novo e em casa. Dessa forma, você aprende o ritmo real. Faça isso da maneira certa e você achará viciante.

A pergunta é a seguinte: o TDD deve ser aplicado a cada módulo,

Esse é o pensamento estrutural. Você não deve dizer coisas como testar todas as funções, classes ou módulos. Esses limites não são importantes para o teste e devem poder mudar de qualquer maneira. TDD é estabelecer uma necessidade comportamental testável e não se importar com a satisfação. Se não fosse, não poderíamos refatorar.

ou seria suficiente aplicá-los apenas aos módulos que não foram comprovadamente corretos?

É suficiente aplicá-los onde você achar necessário. Eu começaria com um novo código. Você obterá muito mais retorno dos testes mais cedo do que tarde. Não faça isso no trabalho até ter praticado o suficiente para dominá-lo em casa.

Quando você mostrar que o TDD é eficaz com o novo código no trabalho e se sentir confiante o suficiente para aceitar o código antigo, eu começaria com o código comprovado. O motivo é porque você poderá ver imediatamente se os testes que está escrevendo estão levando o código em uma boa direção.

Minha principal pergunta é a seguinte: o TDD serviria a algum propósito de código, se eu já puder provar a correção do código?

Os testes não apenas provam a exatidão. Eles mostram intenção. Eles mostram o que é necessário. Eles apontam um caminho para mudar. Um bom teste diz que existem várias maneiras de escrever esse código e obter o que você deseja. Eles ajudam os novos codificadores a ver o que podem fazer sem quebrar tudo.

Somente quando você tiver esse problema, deverá entrar no código não comprovado.

Um aviso contra os fanáticos: você parece ter alcançado sucesso e, portanto, é improvável que dê um pulo na frente. Mas outros que procuram provar a si mesmos não serão tão reservados. TDD pode ser exagerado. É incrivelmente fácil criar um conjunto de testes que realmente prejudica a refatoração porque bloqueiam coisas triviais e sem sentido. Como isso acontece? Porque as pessoas que procuram exibir testes apenas escrevem testes e nunca refatoram. Solução? Faça-os refatorar. Faça-os lidar com mudanças de recursos. Quanto antes melhor. Isso mostrará os testes inúteis rapidamente. Você prova flexibilidade flexionando.

Um aviso contra a categorização estrutural: algumas pessoas insistem que uma classe é uma unidade. Alguns chamarão qualquer teste com duas classes de teste de integração. Alguns insistem que você não pode cruzar o limite x e chamar isso de teste de unidade. Em vez de me preocupar com nada disso, aconselho que você se preocupe com o comportamento do seu teste. Ele pode ser executado em uma fração de segundo? Pode ser executado em paralelo com outros testes (sem efeitos colaterais)? Pode ser executado sem iniciar ou editar outras coisas para satisfazer dependências e pré-condições? Ponho essas considerações à frente se ele se relacionar com um banco de dados, sistema de arquivos ou rede. Por quê? Porque esses três últimos são apenas problemas porque causam os outros problemas. Agrupe seus testes com base em como você pode esperar que eles se comportem. Não são os limites que eles cruzam. Então você sabe o que pode esperar que cada suíte de testes faça.

Vi pessoas dizerem que não querem usar o TDD porque isso teria muito custo adicional, e os apoiadores do TDD o defendem dizendo que quando você se acostuma a escrever TDD o tempo todo, não há muito custo adicional.

Essa pergunta já tem respostas aqui .

candied_orange
fonte
1

O Desenvolvimento Orientado a Testes é mais sobre prototipagem e brainstorming de uma API do que testes. Os testes criados geralmente são de baixa qualidade e, eventualmente, precisam ser descartados. A principal vantagem do TDD é determinar como uma API será usada antes de escrever a implementação da API. Essa vantagem também pode ser obtida de outras maneiras, por exemplo, escrevendo a documentação da API antes da implementação.

As provas de correção são sempre mais valiosas que os testes. Os testes não provam nada. No entanto, para usar provas de correção de maneira produtiva, é útil ter um verificador automático de provas, e você precisará trabalhar com contratos de algum tipo (design por contrato ou design baseado em contrato).

No passado, ao trabalhar em seções críticas do código, tentava provas manuais de correção. Até as provas informais são mais valiosas do que qualquer teste automatizado. Mas você ainda precisa dos testes, a menos que possa automatizar suas provas, pois as pessoas quebrarão seu código no futuro.

Testes automatizados não implicam TDD.

Frank Hileman
fonte
O brainstorming de uma API e o design por contrato já é o modo como abordo os problemas, então gostaria de dizer que gosto da sua resposta, mas outras fontes dizem "No desenvolvimento orientado a testes, cada novo recurso começa com a criação de um teste". Você disse coisas com as quais concordo, mas não me convenceu a usar a abordagem TDD. "Testes automatizados não implicam TDD", ok, mas o que o TDD implica, em vez de simplesmente seguir as melhores práticas gerais?
21418 Kylee
1
Embora eu concorde que o TDD geralmente envolva o brainstorming de uma API, IMO, isso às vezes é uma coisa ruim . A API não deve ser projetada (necessariamente) para testabilidade, deve ser projetada para ser usada sem problemas pelos seus clientes e para fazer sentido para as classes / códigos envolvidos. Freqüentemente eu vi um escritor de testes dizer "vamos adicionar String getSomeValue()aqui para que possamos testá-lo" quando isso não faz sentido para o design geral. Claro, você pode remover essa função mais tarde, mas, na minha experiência, isso é raro.
user949300
1
@ user949300, Existe uma grande sobreposição entre o design de uma API testável e a projetada para ser usada sem problemas pelos seus clientes. Adicionar código desnecessário para o teste mostra que você tem um design incorreto. Com muita freqüência, os escritores de API esquecem a depuração do que está acontecendo de errado com uma API. Escrever algo testável E útil para o usuário obriga a pensar nessas coisas ... sem vazar detalhes da implementação em sua interface.
Berin Loritsch 02/04
@ user949300 A reclamação número um sobre TDD é provavelmente a maneira como a API é modificada para "testabilidade", no sentido da unidade, de modo que coisas que normalmente são encapsuladas sejam expostas. A queixa número dois é provavelmente que ela não cresce bem ao longo do tempo.
21818 Frank Hileman #
Os testes automatizados do @Kylee são mais antigos que a palavra da moda "TDD". A diferença é que testes automatizados são simplesmente o que você acha que deveria ser testado - não há dogma nem ordem específica na qual você os escreve. Também não há ênfase nos testes de unidade versus integração.
21818 Frank Hileman
0

A) Você está lendo o código e se convencendo de que ele está correto não é nem remotamente próximo de provar que está correto. Caso contrário, por que escrever testes?

B) Quando você altera o código, deseja executar testes que demonstram que o código ainda está correto ou não.

Josh
fonte
este parece pontos meramente repetir feitas (e muito melhor explicado) em resposta superior que foi postado algumas semanas antes
mosquito
@gnat É uma resposta mais sucinta para algo que não precisa de um romance escrito sobre isso. Por favor, tente contribuir com algo aqui.
1927 Josh
-1

Vou fazer uma ressalva dizendo que, uma vez que você esteja acostumado a usar o TDD efetivamente, você economizará tempo no final do jogo. É preciso prática para aprender a usar o TDD efetivamente, e não ajuda quando você está com problemas de tempo. Ao aprender a melhor forma de usá-lo, recomendo iniciar um projeto pessoal em que você tenha mais liberdade e menos pressão no cronograma.

Você verá que seu progresso inicial é mais lento enquanto você está experimentando mais e escrevendo sua API. Com o tempo, seu progresso será mais rápido à medida que seus novos testes começarem a passar sem alterar o código, e você terá uma base muito estável para construir. No final do jogo, o código que não é criado usando o TDD exige que você gaste muito mais tempo no depurador enquanto tenta descobrir o que está errado do que seria necessário. Você também corre um risco maior de quebrar algo que costumava trabalhar com novas mudanças. Avalie a eficácia do TDD vs. não o use pelo tempo total até a conclusão.

Dito isto, TDD não é o único jogo na cidade. Você pode usar o BDD, que usa uma maneira padrão de expressar o comportamento de um aplicativo de pilha completa e avaliar a correção da API a partir daí.

Todo o seu argumento depende de "provar a exatidão do código", então você precisa de algo que defina a correção do código. Se você não estiver usando uma ferramenta automatizada para definir o que "correto" significa, a definição é muito subjetiva. Se sua definição de correto é baseada no consenso de seus colegas, isso pode mudar em qualquer dia. Sua definição de correto precisa ser concreta e verificável, o que também significa que deve poder ser avaliado por uma ferramenta. Por que não usar um?

A vitória nº 1 do uso de testes automatizados de qualquer tipo é que você pode verificar se seu código permanece correto mesmo quando os patches do sistema operacional são aplicados de maneira rápida e eficiente. Execute seu conjunto para garantir que tudo esteja passando, aplique o patch e execute o conjunto novamente. Melhor ainda, faça parte da sua infraestrutura de construção automatizada. Agora você pode verificar se seu código permanece correto após mesclar o código de vários desenvolvedores.

Minha experiência com o TDD me levou às seguintes conclusões:

  • É ótimo para código novo, difícil para alterar sistemas legados
  • Você ainda precisa saber o que está tentando realizar (ou seja, ter um plano)
  • Lento para começar, mas economiza tempo depois
  • Força você a pensar em como validar a correção e a depuração da perspectiva do usuário

Minha experiência com o BDD me levou às seguintes conclusões:

  • Funciona tanto para o código legado quanto para o novo código
  • Valida toda a pilha e define a especificação
  • Mais lento para começar a funcionar (ajuda a ter alguém que conhece o conjunto de ferramentas)
  • Menos comportamentos precisam ser definidos que os testes de unidade

Definição de correto: seu código está em conformidade com os requisitos. Isso é melhor verificado com o BDD, que fornece um meio de expressar esses requisitos de maneira legível por humanos e verificá-los em tempo de execução.

Não estou falando de correção em termos de provas matemáticas, o que não é possível. E estou cansado de ter esse argumento.

Berin Loritsch
fonte
Um post maravilhoso que aborda um problema que me levou a considerar o TDD para começar: a definição atual de "correção de código" é de fato um consenso dos pares. Minha preocupação é que, conforme a equipe mude no futuro, esse método pare de funcionar. Mas como o TDD resolveria isso? Embora testes antigos possam impedir que os novos membros da equipe quebrem facilmente as funcionalidades antigas, eles ainda podem escrever testes incompletos para recursos futuros, o que ainda levaria a problemas. No final, ambos os métodos dependem de confiar em sua equipe. Eu nunca ouvi falar do BDD antes, então vou dar uma olhada nisso também. Obrigado.
21418 Kylee
2
"você pode verificar se seu código permanece correto" Não. Nem mesmo perto. Você pode, no máximo, afirmar que o teste que você executou ainda passa.
Bent
@ Kylee, BDD aborda essa preocupação melhor. O BDD solicita que você escreva uma especificação que seja verificável. A especificação é escrita em seu idioma natural, com o objetivo de criar ganchos para o código de teste real que verifica a especificação. Isso faz a ponte entre duas preocupações, comunicando os requisitos reais e aplicando-os.
Berin Loritsch 03/04/19
1
@ Bent, por favor, não discuta sobre "correção" em termos de provas matemáticas. Esse não é o assunto da conversa ou o que eu pretendia transmitir. No entanto, com base na experiência, as pessoas que postam comentários como o seu tendem a ter isso em mente. Você pode verificar absolutamente se o código está em conformidade com os requisitos. Essa é a definição de trabalho correta de que estou falando.
Berin Loritsch 03/04/19
O problema é que você está dirigindo para uma definição de "correto", onde o requisito é que o código passe nos testes que você definiu. Qualquer outro conjunto de entradas é um comportamento indefinido e a saída é arbitrária. Isso realmente não corresponde ao que a maioria dos usuários consideraria serem os requisitos.
Simon B