65.000.000.000 testes para executar

50

Foi-me perguntado sobre como executar um conjunto de 65.000.000.000 de testes e gostaria de saber se é normal ter um projeto com uma quantidade tão grande de testes.

Você já trabalhou em projetos com essa característica?

    
por juanpavergara 10.07.2013 / 20:14
fonte

6 respostas

103

Com 65 bilhões de testes, parece que você está sendo solicitado a testar todas as entradas possíveis. Isso não é útil - você essencialmente testaria se o processador funciona corretamente, e não se o código está correto.

Você deve testar classes de equivalência . Isso reduzirá drasticamente o seu intervalo de entradas de teste.

Considere também se você pode subdividir seu sistema em partes menores. Cada peça será mais fácil de testar isoladamente e, em seguida, você poderá realizar alguns testes de integração que juntam todas as peças.

Se você ainda quiser ter certeza de que algumas dessas combinações de entrada funcionam, talvez você possa tentar o testes de fuzz . Você obterá alguns dos benefícios de testar muitos inputs diferentes, mas sem executar todos os 65 bilhões deles.

    
por 10.07.2013 / 20:38
fonte
39

Se esta for uma suíte de testes real, você não quer chegar nem perto de trabalhar nela.

Todo o trabalho de um testador é encontrar um equilíbrio entre o teste completo o suficiente para ter certeza de que você obteve os resultados "certos" e escrever alguns testes suficientes para que eles possam ser executados em um período de tempo razoável.

Muitos testes podem ser resumidos em "classes de equivalência", o que significa que, em vez de executar 3 bilhões de testes, você executa 1 que oferece um nível razoável de confiança de que todos os outros testes dessa classe de equivalência seriam executados com êxito. desperdiçar o tempo com eles.

Você deve dizer a quem está pensando em executar 65 bilhões de testes que eles precisam para fazer um trabalho melhor, abstraindo os testes em classes de equivalência.

    
por 10.07.2013 / 20:44
fonte
23

Mais do que provável, você chegou ao seu valor de 65 bilhões de testes calculando todas as combinações possíveis de entradas no sistema em teste, ou calculando a complexidade ciclomática e assumindo que um teste deve ser escrito para cada um desses caminhos de execução exclusivos.

Não é assim que testes reais são escritos, porque, como outros pôsteres e comentaristas indicaram, o poder técnico necessário para executar testes de 65 bilhões é impressionante. Isso seria como escrever um teste que exercesse um método para adicionar dois inteiros, ligando cada possível permutação de dois valores de 32 bits e verificando o resultado. É uma insanidade total. Você precisa desenhar a linha e identificar um subconjunto de todos os possíveis casos de teste, o que entre eles garantiria que o sistema se comportaria como esperado em toda a faixa de entradas. Por exemplo. você testa a adição de alguns números "comuns", testa alguns cenários de número negativo, testa limites técnicos, como cenários de estouro, e testa todos os cenários que devam resultar em erro. Como foi mencionado, esses vários tipos de testes exercitam "classes de equivalência"; Eles permitem que você tire uma amostra representativa das possíveis entradas, junto com quaisquer "outliers" conhecidos, e diga com uma confiança extremamente alta de que, como esses cenários passam, todos os cenários semelhantes a esses passarão.

Considere um dos códigos básicos katas, o gerador numeral romano. A tarefa, a ser executada usando técnicas de TDD em um estilo "dojo", é escrever uma função que pode aceitar qualquer número de 1 a 3000 e produzir o numeral romano correto para esse valor numérico.

Você não resolve este problema escrevendo 3000 testes unitários, um de cada vez, e passando-os por vez. Isso é loucura; o exercício normalmente leva entre uma e duas horas e você estaria lá por dias testando cada valor individual. Em vez disso, você fica esperto. Você começa com o caso base mais simples (1 == "I"), implementa isso usando uma estratégia "menor código" ( return "I"; ) e depois procura como o código que você tem se comportará incorretamente em outro cenário esperado (2 == "II"). Enxague e repita; mais do que provável, você substituiu sua implementação inicial por algo que repete o caractere "I" sempre que necessário (como return new String('I',number); ). Isso obviamente passará em um teste para o III, então você não se incomoda; em vez disso, você escreve o teste para 4 == "IV", que você sabe que a implementação atual não fará corretamente.

Ou, em um estilo mais analítico, você examina cada decisão condicional que é feita pelo código (ou precisa ser) e escreve um teste projetado para inserir o código para cada possível resultado de cada decisão. Se você tiver 5 instruções if (cada uma com uma ramificação true e false), cada uma delas totalmente independente da outra, codifique 10 testes, não 32. Cada teste será projetado para afirmar duas coisas sobre uma determinada decisão possível; primeiro que a decisão correta é tomada e, em seguida, que o código inserido, desde que a condição esteja correta. Você não codifica um teste para cada possível permutação de decisões independentes. Se as decisões forem dependentes, você terá que testar mais delas em combinação, mas há menos combinações desse tipo porque algumas decisões só são tomadas quando outra decisão teve um resultado específico.

    
por 10.07.2013 / 21:32
fonte
5

Isso é "normal"? Onde "normal" é definido como a experiência média ou típica. Não posso dizer que já tive que trabalhar em um projeto como esse, mas estive em um projeto onde um em cada poucos milhões de bits seria invertido. Testando que um foi ... um desafio.

É potencialmente necessário? Bem, isso depende das garantias e especificidades do projeto. É um pouco incrédulo compreender no começo, mas sua pergunta é leve em detalhes.

Como outros (MichaelT) apontaram, o tempo para concluir esta tarefa com testes em série torna isso impraticável. Então, a paralelização se torna sua primeira consideração. Quantos sistemas de teste você pode lançar com esse problema, e que suporte você tem para agrupar os resultados desses múltiplos sistemas?

Quais são as garantias de que o dispositivo ou algoritmo que você está testando está sendo replicado de maneira confiável? O software é bastante confiável na replicação, mas os dispositivos de hardware (especialmente a primeira geração) podem ter problemas de fabricação. Uma falsa falha de teste nesse caso poderia indicar um algoritmo inválido ou o dispositivo não montou corretamente. Você precisa distinguir entre esses dois casos?

Você também precisará considerar como validará os próprios sistemas de teste. Presumindo uma razão legítima para muitos casos de teste, você precisará de muita automação. Essa automação precisa ser inspecionada para garantir que ela não erre na geração dos casos de teste. Verificações pontuais de erros seriam realmente o equivalente a encontrar uma agulha no palheiro.

Este link arstechnica pode ou não lançar alguma visão sobre suas considerações de teste. Os clusters de GPU são comumente usados para senhas de cracking de força bruta. A citada no artigo pode can cycle through as many as 350 billion guesses per second , então isso coloca seus testes 65B em perspectiva. É provável que seja um domínio diferente, mas mostra como abordar a tarefa de diferentes ângulos pode produzir uma solução viável.

    
por 10.07.2013 / 20:50
fonte
3

Eu não acho viável manter 6.5e + 10 testes em primeiro lugar, então executá-los pode ser irrelevante. Mesmo os maiores projetos, como o Debian com todos os seus pacotes, têm apenas algumas centenas de milhões de SLOCs totais.

Mas se você tiver que executar um grande número de testes, existem algumas estratégias.

  • Não execute todos eles. Provavelmente, nem todo teste depende de cada caminho de código. Defina dependências entre os subsistemas e seus testes, e entre os conjuntos de testes, e você poderá executar somente testes de unidade relevantes para uma mudança específica, somente os testes de integração dependendo desses testes de unidade, etc.

  • Execute-os em paralelo. Com uma base de código tão grande, você provavelmente tem um farm de construção massivo (de volta à JetBrains, uma operação relativamente pequena, costumávamos ter 40-50 builds em execução no farm de integração / construção contínua da IDEA). Como os testes de unidade são independentes e os testes de integração podem reutilizar códigos já construídos, os testes são relativamente fáceis de serem paralelizados.

  • Pare de correr cedo. Se você sabe que um conjunto de testes específico depende do funcionamento razoável da correção de outro conjunto de testes, é possível cortar toda a cadeia depois que um link falhar.

Aviso: não sou engenheiro de testes profissional. Pegue o acima com um grão de sal.

    
por 10.07.2013 / 21:01
fonte
0

Embora tenha havido várias boas sugestões aqui sobre como tentar se infiltrar com menos testes, duvido seriamente que seu sistema tenha apenas 65 bilhões de combinações de entrada. Isso é menos que 36 bits de entrada. Vamos supor que você já tenha tomado todos os conselhos dados acima.

Se cada teste levar cerca de um milissegundo para ser executado e você distribuir os testes em apenas 10 processadores (um PC normal), o teste será executado em pouco mais de 69 dias. Isso é um tempo, mas não completamente irracional. Distribua entre 100 processadores (uma dúzia de PCs normais ou um PC servidor razoável) e os testes serão concluídos em menos de 7 dias. Você poderia executá-los a cada semana para verificar se há regressões.

    
por 13.07.2013 / 14:52
fonte