Me perguntaram sobre como executar um conjunto de 65.000.000.000 testes e me pergunto se é normal ter um projeto com uma quantidade tão grande de testes.
Você já trabalhou em projetos com essa característica?
Me perguntaram sobre como executar um conjunto de 65.000.000.000 testes e me pergunto se é normal ter um projeto com uma quantidade tão grande de testes.
Você já trabalhou em projetos com essa característica?
Respostas:
Com 65 bilhões de testes, parece que você está sendo solicitado a testar todas as entradas possíveis. Isso não é útil - você basicamente testaria se o seu processador funciona corretamente, não se o seu código está correto.
Você deveria estar testando classes de equivalência . Isso reduzirá drasticamente sua gama 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á executar alguns testes de integração que juntam todas as peças.
Se você ainda deseja a garantia de que algumas dessas combinações de entradas funcionam, talvez possa tentar o teste de fuzz . Você obterá alguns dos benefícios de testar muitas entradas diferentes, mas sem executar todos os 65 bilhões deles.
Se esse é um conjunto de testes real, você não quer chegar nem perto de trabalhar nele.
Todo o trabalho de um testador é encontrar um equilíbrio entre testar completamente o suficiente para ter certeza de que você obteve os resultados "certos" e escrever poucos testes suficientes para que possam ser executados em um período de tempo razoável.
Muitos testes podem ser abstraídos para "classes de equivalência", o que significa que, em vez de executar 3 bilhões de testes, você executa 1 que fornece um nível razoável de confiança de que todos os outros testes nessa classe de equivalência seriam executados com êxito, se você decidisse desperdiçar o tempo executando-os.
Você deve dizer a quem estiver pensando em executar 65 bilhões de testes que eles precisam fazer um trabalho melhor abstraindo os testes em classes de equivalência.
Mais do que provável, você chegou ao seu número 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 gravado para cada um desses caminhos de execução exclusivos.
Não é assim que os testes reais são escritos, porque, como outros pôsteres e comentadores indicaram, o poder técnico necessário para executar 65 bilhõestestes é impressionante. Isso seria como escrever um teste que exercite um método para adicionar dois números inteiros, conectando todas as permutações possíveis de dois valores de 32 bits e verificando o resultado. É insanidade absoluta. É necessário traçar a linha e identificar um subconjunto de todos os casos de teste possíveis, o que entre eles garantiria que o sistema se comportasse conforme o esperado em toda a gama de entradas. Por exemplo. você testou a adição de alguns números "comuns", alguns cenários de números negativos, limites técnicos como cenários de estouro e todos os cenários que deveriam resultar em erro. Como foi mencionado, esses vários tipos de testes exercem "classes de equivalência"; eles permitem que você colete uma amostra representativa das possíveis entradas, juntamente com os "outliers" conhecidos,
Considere um dos katas de código básico, o Gerador de Números Romanos. A tarefa, a ser executada usando técnicas TDD no estilo "dojo", é escrever uma função que possa aceitar qualquer número de 1 a 3000 e produzir o número romano correto para esse valor numérico.
Você não resolve esse problema escrevendo 3000 testes de unidade, um de cada vez, e passando-os sucessivamente. Isso é loucura; o exercício normalmente leva entre uma e duas horas e você fica 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 de "código mínimo" ( return "I";
) e depois procura como o código que você possui 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" quantas vezes for necessário (como return new String('I',number);
). Obviamente, isso passará em um teste para III, para que você não se incomode; em vez disso, você escreve o teste para 4 == "IV", que você sabe que a implementação atual ganhou '
Ou, em um estilo mais analítico, você examina cada decisão condicional tomada pelo código (ou precisa ser) e escreve um teste desenvolvido para inserir o código para cada resultado possível de cada decisão. Se você tiver cinco instruções if (cada uma com uma ramificação verdadeira e falsa), cada uma delas totalmente independente da outra, você codifica 10 testes, não 32. Cada teste será projetado para afirmar duas coisas sobre uma possível decisão específica; primeiro que a decisão correta é tomada e, em seguida, que o código digitado, desde que a condição esteja correta. Você não codifica um teste para cada permutação possível de decisões independentes. Se as decisões são dependentes, é necessário testar mais delas em combinação, mas há menos combinações, porque algumas decisões só são tomadas quando outra decisão tem um resultado específico.
Isso é "normal" ?, não. 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 já estive em um projeto em que um em cada milhão de bits seria invertido. Testar esse foi ... um desafio.
É potencialmente necessário? Bem, isso depende das garantias e especificidades do projeto. É um pouco incrédulo de 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. Portanto, a paralelização se torna sua primeira consideração. Quantos sistemas de teste você pode solucionar esse problema e que apoio você tem para agrupar os resultados desses vários sistemas?
Que garantias você tem de que o dispositivo ou algoritmo que você está testando está sendo replicado com segurança? 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 falha de teste falso nesse caso pode indicar um algoritmo incorreto ou o dispositivo não foi montado 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 não haja erros na geração de seus casos de teste. A verificação pontual de erros seria realmente o equivalente a encontrar uma agulha no palheiro.
Esse link da arstechnica pode ou não fornecer algumas dicas sobre as considerações de teste. Os clusters de GPU são comumente usados para senhas de quebra de força bruta. O citado no artigo pode can cycle through as many as 350 billion guesses per second
, de modo que coloca seus testes de 65B em perspectiva. Provavelmente, é um domínio diferente, mas mostra como abordar a tarefa de diferentes ângulos pode gerar uma solução viável.
Eu não acho que é viável manter os testes 6.5e + 10 em primeiro lugar, portanto, executá-los pode ser discutível. Até os maiores projetos, como o Debian com todos os seus pacotes, têm apenas várias centenas de milhões de SLOCs no total.
Mas se você precisar executar um grande número de testes, existem algumas estratégias.
Não execute todos eles. Provavelmente nem todos os testes dependem de todos os caminhos de código. Defina as dependências entre os subsistemas e seus testes, e entre os conjuntos de testes, e você poderá executar apenas testes de unidade relevantes para uma alteração específica, apenas 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 build massivo (de volta ao JetBrains, uma operação relativamente pequena, costumávamos ter 40 a 50 agentes de build sendo executados apenas no farm de integração / integração contínuo da IDEA). Como os testes de unidade são independentes e os testes de integração podem reutilizar o código já criado, é relativamente fácil paralelizar os testes.
Pare de correr cedo. Se você sabe que um conjunto de testes específico depende, para o seu funcionamento razoável, da correção de outro conjunto de testes, você pode cortar toda a cadeia depois de ver um link falhar.
Disclaimer: Eu não sou um engenheiro de teste profissional. Pegue o acima com um grão de sal.
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 entradas. Isso é menos de 36 bits de entrada. Vamos supor que você já tenha seguido todos os conselhos dados acima.
Se cada teste demorar 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 servidor razoável) e os testes serão concluídos em menos de 7 dias. Você pode executá-las toda semana para verificar se há regressões.