100% de cobertura de código é um sonho?


28

É possível esperar 100% de cobertura de código em aplicativos da web pesados ​​jquery / backbonejs? É razoável falhar em um sprint porque a cobertura de 100% não é alcançada quando a cobertura de código real gira em torno de 92% a 95% em javascript / jquery?


7
Sons “Fail um sprint” estranhamente sinistra ...
Donal Fellows

5
É uma assíntota.
Robert Harvey

12
mesmo se você tiver uma cobertura completa alguns bugs não será encontrada por isso não contar com esse número para resolver tudo
catraca aberração

11
Tudo é possível. A verdadeira questão é se o valor da cobertura de código de 100% vale o custo em tempo e recursos.
JohnFx

5
Por que você está se preocupando com isso, quando a suposição subjacente - que 100% (ou qualquer outro número) de cobertura automatizada de teste melhorará magicamente o seu código - é um sonho em si?
Mason Wheeler

Respostas:


30

É igualmente realista, pois não é realista.

Realista
Se você tem testes automatizados que demonstram cobrir toda a base de códigos, é razoável insistir na cobertura de 100%.
Também depende de quão crítico é o projeto. Quanto mais crítico, mais razoável esperar / exigir a cobertura completa do código.
É mais fácil fazer isso em projetos de tamanho pequeno a médio.

Irrealista
Você está começando com 0% de cobertura ...
O projeto é monstruoso com muitos e muitos caminhos de erro difíceis de recriar ou disparar.
A gerência não está disposta a comprometer / investir para garantir que a cobertura esteja lá.

Já trabalhei em diversos projetos, desde cobertura a decente. Nunca um projeto com 100%, mas certamente houve momentos em que desejei que tivéssemos mais perto de 100% de cobertura.
Em última análise, a questão é se a cobertura existente atende aos casos necessários o suficiente para que a equipe se sinta confortável em enviar o produto.

Como não sabemos o impacto de uma falha no seu projeto, não podemos dizer se 92% ou 95% é suficiente ou se 100% é realmente necessário. Ou, nesse caso, o 100% testa completamente tudo o que você espera.


30
... E só porque você tem 100% de cobertura de código não significa que você tem 100% de cobertura de filiais, portanto, mesmo com 100% de cobertura de código, você pode estar perdendo muito.
Bryan Oakley

3
+1 para o tamanho do projeto. A divisão em componentes menores, reutilizáveis ​​e testáveis ​​nos permitiu obter ~ 95% de cobertura. 100% de cobertura não é necessária. Os testes de integração devem cobrir lacunas nos testes de unidade.
Apoorv Khurasia

5
@BryanOakley ... e também seus testes poderia ser inútil, ou não mesmo teste de qualquer coisa
David_001

5
@BryanOakley E mesmo com 100% de cobertura das filiais, é possível que uma certa combinação de filiais possa causar um problema. (duas sequencial IF, por exemplo, pode ser ramificado e em torno em testes separados, mas faltando um teste que entra em ambos cobertura ramo completa, ainda um caminho de execução é desperdiçada.)
Izkata

4
Mesmo 100% de cobertura da filial, incluindo todos os caminhos de execução, não é suficiente. Talvez algum erro ocorra apenas quando você usa uma combinação de ramificações e tem alguma entrada externa, digamos uma data malformada. Não há possibilidade de que todos os casos sejam cobertos. Ao mesmo tempo, pode-se ter uma boa confiança com menos de 100% de cobertura, mas casos de borda adequadamente escolhidos como entrada.
Andrea

32

Quem testa os testes?

É muito ingênuo na melhor das hipóteses e irrealista, mesmo no sentido teórico e impraticável no sentido comercial.

  • Não é realista com código que possui alta complexidade ciclomática. Existem muitas variáveis ​​para cobrir todas as combinações.
  • Não é realista com código fortemente concorrente. O código não é determinístico, portanto você não pode cobrir todas as condições que possam ocorrer, pois o comportamento será alterado a cada execução de teste.
  • Não é realista no sentido comercial, apenas paga dividendos para escrever testes de código que é o código do caminho crítico, que é importante e que pode mudar com freqüência.

Testar todas as linhas de código não é uma boa meta

É muito caro escrever testes, é um código que precisa ser escrito e testado, é um código que deve ser documentado no que realmente está tentando testar, é um código que deve ser mantido com alterações na lógica de negócios e os testes falham porque estão desatualizados. Manter testes automatizados e a documentação sobre eles pode ser mais caro do que manter o código às vezes.

Isso não quer dizer que o teste de unidade e os testes de integração não sejam úteis, mas apenas onde eles fazem sentido, e fora dos setores que podem matar pessoas, não faz sentido tentar testar todas as linhas de código em uma base de código. Fora dessas mortes críticas, muitas pessoas codificam rapidamente as bases, é impossível calcular um retorno positivo do investimento que 100% da cobertura de código implicaria.

Problema de parada:

Na teoria da computabilidade, o problema de parada é o problema de determinar, a partir de uma descrição de um programa de computador arbitrário e de uma entrada, se o programa terminará a execução ou continuará sendo executado para sempre.

Alan Turing provou em 1936 que um algoritmo geral para resolver o problema de parada de todos os pares possíveis de entrada de programa não pode existir. Uma parte essencial da prova foi uma definição matemática de um computador e programa, que ficou conhecida como máquina de Turing; o problema da parada é indecidível nas máquinas de Turing. É um dos primeiros exemplos de um problema de decisão.

Como você não pode nem provar que algo funciona 100%, por que esse é seu objetivo?

Puro e simples, na maioria dos casos, não faz sentido nos negócios.


7
isso realmente precisa ser a resposta aceita. 100% de cobertura de código é quase tão ruim quanto 0%.
Ryathal 15/06/12

1
"Existem muitas variáveis ​​para cobrir todas as combinações." Isso não tem nada a ver com obter 100% de cobertura de código. Se uma linha de código foi importante o suficiente para ser escrita, e é importante o suficiente para se manter por perto, é importante o suficiente para ser coberta por um teste. Se não estiver coberto por um teste, a única suposição segura é que não funciona. É verdade que, para alguns códigos, não faz sentido do ponto de vista comercial testá-lo. Esse é o mesmo código que não fazia sentido do ponto de vista comercial para escrever.
still_dreaming_1

2
então você acha que escrever casos de teste para cobrir getXXX()/setXXX()construtores simples de atribuição simples para objetos de valor é um bom uso de tempo e recursos, desculpe esse não é o caso na realidade e uma opinião extremamente ingênua que carece de experiência no mundo real para fazer o backup. Lembre-se de que o código de teste ainda é um código que precisa ser mantido. Quanto menos código você escrever para resolver um problema, melhor em todos os casos .

Uhm "Não é realista com código que possui alta complexidade ciclomática. Existem muitas variáveis ​​para cobrir todas as combinações." - é claro, é por isso que você precisa dividir esse código em partes menores, com pequena complexidade ciclomática e, portanto, mais fáceis de testar. A refatoração dessa maneira é essencial para os testes - ela facilita os testes.
Predrag Stojadinović 22/11

17

Na maioria dos casos, 100% de cobertura do código significa que você "trapaceou" um pouco:

  • Partes complexas e frequentemente alteradas do sistema (como a GUI) foram movidas para modelos declarativos ou outras DSLs.
  • Todo o código que toca nos sistemas externos foi isolado ou manipulado por bibliotecas.
  • O mesmo vale para qualquer outra dependência, principalmente as que exigem efeitos colaterais.

Basicamente, as partes difíceis de testar foram desviadas para áreas onde elas não contam necessariamente como "código". Nem sempre é realista, mas observe que, independentemente de ajudá-lo a testar, todas essas práticas tornam sua base de código mais fácil de trabalhar.


Como está mudando as coisas para os DSLs?
back2dos

2
@ back2dos - Embora você possa fazer um teste de unidade, digamos, em seus scripts python incorporados, provavelmente não está testando em unidade seus modelos de html, ou seu CSS, nem contando as linhas neles para estimativas de cobertura.
precisa

12

Para um exemplo impressionante do mundo real de 100% de cobertura de filial , consulte Como o SQLite é testado .

Sei que sua pergunta é específica sobre javascript, que é um tipo de produto de software totalmente diferente, mas quero conscientizar o que pode ser feito com motivação suficiente.


9

A cobertura 100% do código para testes de unidade para todas as partes de uma aplicação específica é um sonho, mesmo em novos projetos. Eu gostaria que fosse o caso, mas às vezes você simplesmente não pode cobrir um pedaço de código, por mais que tente abstrair dependências externas. Por exemplo, digamos que seu código precise chamar um serviço da web. Você pode ocultar as chamadas de serviço da web atrás de uma interface para poder zombar dessa peça e testar a lógica de negócios antes e depois do serviço da web. Mas a parte real que precisa chamar o serviço da web não pode ser testada em unidade (muito bem mesmo). Outro exemplo é se você precisa se conectar a um servidor TCP. Você pode ocultar o código que se conecta a um servidor TCP atrás de uma interface. Mas o código que se conecta fisicamente a um servidor TCP não pode ser testado em unidade, porque se estiver inativo por algum motivo, isso causaria falha no teste de unidade. E os testes de unidade sempre devem passar, não importa quando são chamados.

Uma boa regra geral é que toda a sua lógica de negócios deve ter 100% de cobertura de código. Mas as peças que precisam invocar componentes externos devem ter o mais próximo possível de 100% de cobertura de código. Se você não pode alcançar, eu não suaria muito.

Muito mais importante, os testes estão corretos? Eles refletem com precisão seus negócios e os requisitos? Ter cobertura de código apenas para ter cobertura de código não significa nada, se você estiver testando incorretamente ou testando código incorreto. Dito isto, se seus testes forem bons, ter uma cobertura de 92 a 95% é excelente.


1
Testar o que acontece quando você obtém combinações estranhas de casos de erro e falhas na resposta pode ser excepcionalmente complicado.
Donal Fellows

Não está entendendo o que o seu sistema fará quando for apresentado a esses problemas complicados como parte do apelo do teste de unidade? Além disso, há um pouco de confusão aqui entre testes de unidade e testes de integração.
Peter Smith

isso entra unit testingem conflito com o integration testingcódigo de teste que você não escreveu está integrationtestando. A pilha TCP está no SO que você não deve testar, deve assumir que já foi testada por quem já a escreveu.

4

Eu diria que, a menos que o código seja projetado com o objetivo específico de permitir 100% de cobertura de teste, 100% poderá não ser possível. Uma das razões seria que, se você codifica defensivamente - o que deveria -, às vezes deve ter um código que lide com situações que você tem certeza de que não deveriam estar acontecendo ou que não podem estar acontecendo, devido ao seu conhecimento do sistema. Cobrir esse código com testes seria muito difícil por definição. Não ter esse código pode ser perigoso - e se você estiver errado e essa situação acontecer uma vez em cada 256? E se houver uma mudança no local não relacionado que torne possível a coisa impossível? Etc. Portanto, 100% pode ser um pouco difícil de alcançar por meios "naturais" - por exemplo, se você tiver um código que aloca memória e um código que verifique se houve falha, a menos que você zombe do gerenciador de memória (o que pode não ser fácil) e escreva um teste que retorne "falta de memória", cobrindo esse código pode ser difícil. Para o aplicativo JS, pode ser uma codificação defensiva em torno de possíveis peculiaridades do DOM em diferentes navegadores, possíveis falhas de serviços externos, etc.

Então, eu diria que devemos nos esforçar para estar o mais próximo possível de 100% e ter uma boa razão para o delta, mas não veria não receber exatamente 100% como necessariamente falha. 95% pode ser bom em um grande projeto, dependendo dos 5%.


Só porque o código não deve ser executado em produção em circunstâncias normais, não significa que ele não possa ser gravado de maneira a ser executado pelos testes. Quão crítico é para esse código incomum ser executado corretamente? É importante o suficiente para cobri-lo por testes? Eu diria que, se não é importante o suficiente para cobrir os testes, não é importante o suficiente para lidar com esse caso. Código que não precisa de testes é um código que não precisa existir e deve ser excluído.
still_dreaming_1

2

Se você está começando com um novo projeto e está usando estritamente uma metodologia de teste primeiro, é perfeitamente razoável ter 100% de cobertura de código, no sentido de que todo o seu código será invocado em algum momento em que seus testes tiverem sido realizados. foi executado. No entanto, talvez você não tenha testado explicitamente todos os métodos ou algoritmos individuais diretamente devido à visibilidade do método e, em alguns casos, pode não ter testado alguns métodos, mesmo que indiretamente.

Obter 100% do seu código testado é potencialmente um exercício caro, principalmente se você não projetou seu sistema para atingir esse objetivo e, se você está concentrando seus esforços de projeto na testabilidade, provavelmente não está prestando atenção suficiente para projetar seu aplicativo para atender a requisitos específicos, principalmente quando o projeto é grande. Sinto muito, mas você simplesmente não pode ter as duas coisas sem que algo seja comprometido.

Se você estiver introduzindo testes em um projeto existente em que os testes não foram mantidos ou incluídos antes, é impossível obter 100% de cobertura do código sem que os custos do exercício superem o esforço. O melhor que você pode esperar é fornecer cobertura de teste para as seções críticas do código que são chamadas mais.

É razoável falhar em um sprint porque a cobertura de 100% não é alcançada quando a cobertura de código real gira em torno de 92% a 95% em javascript / jquery?

Na maioria dos casos, eu diria que você só deve considerar o seu sprint 'falhado' se não atingir seus objetivos. Na verdade, prefiro não pensar nos sprints como falhos nesses casos, porque você precisa aprender com os sprints que não atendem às expectativas, a fim de acertar seu planejamento na próxima vez que definir um sprint. Independentemente disso, não acho razoável considerar a cobertura do código um fator no sucesso relativo de um sprint. Seu objetivo deve ser fazer o suficiente para que tudo funcione conforme especificado e, se você estiver codificando o teste primeiro, poderá sentir-se confiante de que seus testes suportarão esse objetivo. Qualquer teste adicional que você precise adicionar é efetivamente um revestimento de açúcar e, portanto, uma despesa adicional que pode impedi-lo de concluir satisfatoriamente seus sprints.


"Sinto muito, mas você simplesmente não pode ter os dois lados sem que algo seja comprometido." Isso não é verdade. Você sempre pode reduzir os recursos ou ir mais devagar. Se algo não vale a pena testar, não vale a pena escrever. Se uma linha de código é importante o suficiente para se manter por perto, é importante o suficiente para testar. Se não é importante o suficiente para testar, não é importante o suficiente para se manter por perto. A única suposição segura de uma linha de código que não é testada é que ela não funciona.
still_dreaming_1

@ still_dreaming_1, você parece ter apoiado minha declaração e se contradito. Reduzir recursos ou alterar seus prazos são compromissos, cada um dos quais pode afetar o custo do projeto e as expectativas das partes interessadas. Testar código legado que ainda não foi totalmente testado anteriormente é extremamente difícil, pois você precisa entender não apenas o código enquanto ele é executado, mas as intenções do criador original e escrever testes que capturam o comportamento existente do código legado não necessariamente mostram que o código funciona inteiramente como deveria.
S.Robins 12/09

Acho que meu argumento foi que o que foi comprometido, os recursos ou alterações que ainda não foram criados porque o desenvolvimento está se movendo mais rápido, não é um compromisso real, porque se você perder a cobertura para se mover mais rapidamente, esses recursos e alterações poderão apenas se supõe que não funcione corretamente de qualquer maneira. Então, qual foi o objetivo de fazer essas alterações ou adicionar esses recursos, se não importa se eles funcionam corretamente ou não? Se não importa se eles funcionam ou não, essas alterações não precisam ser feitas e agora devem ser retiradas do código.
still_dreaming_1

Eu não acredito mais nisso, ou pelo menos percebo o aspecto prático da verdade que você está falando, especialmente em uma base de código herdada, de modo que é apenas uma explicação do ponto que eu estava tentando enfatizar na época. Na verdade, agora estou completamente em conflito por fazer TDD o tempo todo em uma nova base de código, sem falar em obter 100% de cobertura. Por um lado, toda forma de lógica e razão me diz que essas duas coisas devem ser boas, e, na prática, não consigo torná-la prática. Então, algo está muito errado com o mundo da programação, precisamos de um novo paradigma.
still_dreaming_1

1

Naturalmente, não faço isso, mas o fiz em dois grandes projetos. Se você tem uma estrutura para testes de unidade configurada de qualquer maneira, não é exatamente difícil, mas adiciona muitos testes.

Existe algum obstáculo em particular que está impedindo que você atinja as últimas linhas? Caso contrário, se é fácil obter uma cobertura de 95% a 100%, é melhor você fazê-lo. Desde que você está aqui perguntando, eu vou assumir que não é alguma coisa. O que é isso?


Esta é uma das melhores respostas aqui. Perguntar o que impede uma linha de código de ser facilmente coberta é uma boa pergunta. Cobrir essas linhas forçará você a melhorar o código para que isso aconteça, para que seja uma vitória, uma vitória.
still_dreaming_1

0

92% está bem. Eu sinto que as perguntas reais são:

  • 92% é a 'nova' norma agora? Se o próximo sprint tiver 88% de teste, tudo bem? Esse é frequentemente o início dos conjuntos de testes sendo abandonados.

  • Quão importante é que o software funcione e não tenha bugs. Você tem testes por esses motivos, não "por uma questão de teste"

  • Existe um plano para voltar e preencher os testes ausentes?

  • Por que você está testando? Parece que o foco é% da linha coberta e não a funcionalidade


"Quão importante é que o software funcione e não tenha bugs"? Boa pergunta. Qual é a definição de um bug? Algo que não funciona como pretendido. Se estiver certo que algum código não funcione corretamente, não o escreva. O ponto principal do código é que ele funcione.
still_dreaming_1

0

Martin Fowler escreve em seu blog :I would be suspicious of anything like 100% - it would smell of someone writing tests to make the coverage numbers happy, but not thinking about what they are doing.

No entanto, existem até padrões que exigem 100% de cobertura no nível da unidade. Por exemplo, é um dos requisitos das normas da comunidade europeia de voos espaciais (ECSS, Cooperação Europeia para Padronização Espacial). O artigo vinculado aqui conta uma interessante história do projeto que tinha como objetivo atingir 100% de cobertura de teste em um software já concluído. É baseado em entrevistas com os engenheiros envolvidos que desenvolveram os testes de unidade.

Algumas das lições são:

  • 100% de cobertura é incomum, mas alcançável
  • Às vezes, é necessária uma cobertura de 100%
  • 100% de cobertura traz novos riscos
  • Não otimize para o método 100%
  • Desenvolver uma estratégia adequada para maximizar a cobertura
  • 100% de cobertura não é uma condição suficiente para uma boa qualidade

0

Talvez perguntar se é viável e razoável não seja a pergunta mais útil a ser feita. Provavelmente a resposta mais prática é a resposta aceita. Vou analisar isso em um nível mais filosófico.

Seria ideal uma cobertura de 100%, mas, idealmente, não seria necessário ou seria muito mais fácil de alcançar. Prefiro pensar se é natural e humano do que viável ou razoável.

O ato de programar corretamente é quase impossível com as ferramentas atuais. É muito difícil escrever código totalmente correto e sem bugs. Simplesmente não é natural. Portanto, sem outra opção óbvia, passamos a técnicas como TDD e cobertura de código de rastreamento. Mas, enquanto o resultado final ainda for um processo não natural, será difícil conseguir que as pessoas o façam de maneira consistente e feliz.

Conseguir 100% de cobertura de código é um ato não natural. Para a maioria das pessoas, forçá-las a conseguir isso seria uma forma de tortura.

Precisamos de processos, ferramentas, linguagens e código que mapeiem nossos modelos mentais naturais. Se não fizermos isso, não há como testar a qualidade de um produto.

Basta olhar para todo o software disponível hoje em dia. A maioria mexe bastante regularmente. Não queremos acreditar nisso. Queremos acreditar que nossa tecnologia é mágica e nos faz felizes. E assim escolhemos ignorar, desculpar e esquecer na maioria das vezes que nossa tecnologia falha. Mas se fizermos uma avaliação honesta das coisas, a maior parte do software disponível hoje em dia é bem ruim.

Aqui estão alguns esforços para tornar a codificação mais natural:

https://github.com/jcoplien/trygve

https://github.com/still-dreaming-1/PurposefulPhp

O posterior é extremamente incompleto e experimental. Na verdade, é um projeto que eu comecei, mas acredito que seria um grande passo à frente na arte da programação se eu pudesse dedicar algum tempo para concluí-la. Basicamente, é a ideia de que se os contratos expressam os únicos aspectos de um comportamento de classe com o qual nos preocupamos e já estamos expressando contratos como código, por que não apenas temos as definições de classe e método junto com os contratos. Dessa forma, os contratos seriam o código e não precisaríamos implementar todos os métodos. Deixe a biblioteca descobrir como honrar os contratos para nós.


-2

Atingir 100% no novo código deve ser muito viável e, se você estiver praticando TDD, provavelmente atingirá isso por padrão, pois está deliberadamente escrevendo testes para cada linha de código de produção.

No código legado existente que foi escrito sem testes de unidade, pode ser difícil, pois muitas vezes o código legado não foi escrito com o teste de unidade em mente e pode exigir muita refatoração. Esse nível de refatoração geralmente não é prático, dadas as realidades de risco e cronograma, de modo que você faz trocas.

Na minha equipe, especifico 100% de cobertura do código e, se houver menos do que isso na revisão do código, o proprietário técnico do componente discute por que 100% não foi alcançado com o desenvolvedor e deve concordar com o raciocínio do desenvolvedor. Geralmente, se houver um problema de atingir 100%, o desenvolvedor conversará com o proprietário técnico antes da revisão do código. Descobrimos que uma vez que você adquira o hábito e aprenda técnicas para solucionar vários problemas comuns, adicione testes ao código legado que atingir 100% regularmente não é tão difícil quanto você pensaria inicialmente.

O livro de Michael Feather, " Trabalhando efetivamente com o código legado ", tem sido inestimável para nós por apresentarmos estratégias para adicionar testes ao nosso código legado.


-3

Não, não é possível e nunca será. Se fosse possível, toda a matemática cairia no finitismo. Por exemplo, como você testaria uma função que pegasse dois números inteiros de 64 bits e os multiplicasse? Esse sempre foi meu problema ao testar e provar que um programa estava correto. Para qualquer coisa, exceto os programas mais triviais, o teste é basicamente inútil, pois cobre apenas um pequeno número de casos. É como checar 1.000 números e dizer que você provou a conjectura de Goldbach.


Oh! Então, alguém está chateado por eu não ter respondido ao problema no plano de sua concepção; testar é um desperdício ... não me importo se é popular. Isso nunca vai funcionar. Eu não posso. Os cientistas da computação mais inteligentes sabem disso (Dijkstra, Knuth, Hoare et al.). Acho que se você é um programador JavaScript que xinga a Programação eXtreme, não se importa com essas manivelas. Blah, tanto faz, quem se importa ... escreva códigos ruins. Desperdice CO ^ 2 executando seus testes. - Quero dizer, quem tem tempo para sentar e pensar mais? Nós exportamos isso para o computador.
veryfoolish

3
A pergunta está marcada com "TDD". O TDD é mais uma ferramenta de design e uma ferramenta de exploração de problemas do que uma de teste, e cada "teste" é apenas um exemplo de como o código se comportará em algum contexto, para que as pessoas possam ler e entender o que está acontecendo, depois alterá-lo com segurança . O TDD bem executado tende a levar a códigos mais limpos e fáceis de usar, e a execução dos testes apenas verifica se a documentação está atualizada. A maioria dos pacotes TDD quase nunca captura bugs; não é para isso que eles estão lá. Acho que você está sendo prejudicado porque sua resposta trai essa falta de entendimento, e espero que esse comentário ajude com isso.
Lunivore 20/08/2012
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.