A "teoria dos testes de software" no Google parece apenas dar teorias no sentido suave da palavra; Não consegui encontrar nada que classificasse como teoria no sentido matemático, da informação teórica ou de algum outro campo científico.
O que estou procurando é algo que formalize o que é testar, as noções usadas, o que é um caso de teste, a viabilidade de testar algo, a praticidade de testar algo, a extensão em que algo deve ser testado, definição formal / explicação de cobertura de código etc.
UPDATE: Além disso, não tenho certeza, intuitivamente, sobre a conexão entre a verificação formal e o que pedi, mas há claramente algum tipo de conexão.
testing
math
theory
formal-methods
information-theory
Erik Kaplun
fonte
fonte
double pihole(double value) { return (value - Math.PI) / (value - Math.PI); }
que aprendi com meu professor de matemática . Esse código tem exatamente um buraco , que não pode ser descoberto automaticamente apenas a partir de testes em caixas pretas. Em Math, não existe esse buraco. No cálculo, você pode fechar o buraco se os limites unilaterais forem iguais.(a,b)=>a/b
, que precisa ser estendida com um valor de estouro, para ser corretamente composta.Respostas:
Para um livro que explora a matemática por trás dos testes de software ... o livro seminal a ser obtido é A arte da análise de desempenho de sistemas de computadores: técnicas para projeto experimental, medição, simulação e modelagem
Embora tenha sido publicado pela primeira vez em 1991, ainda é popular hoje em dia porque é um livro de matemática aplicada que se concentra exclusivamente em análises, simulações e medições de desempenho.
fonte
Não posso apontar um bom recurso on-line (os artigos da Wikipedia em inglês sobre esses tópicos tendem a ser improváveis), mas posso resumir uma palestra que ouvi que também abordou a teoria básica dos testes.
Modos de teste
Existem diferentes classes de testes, como testes de unidade ou testes de integração . Um teste de unidade afirma que um pedaço de código coerente (função, classe, módulo) obtido por conta própria funciona como esperado, enquanto um teste de integração afirma que vários desses pedaços funcionam corretamente juntos.
Um caso de teste é um ambiente conhecido no qual uma parte do código é executada, por exemplo, usando entrada de teste específica ou zombando de outras classes. O comportamento do código é então comparado ao comportamento esperado, por exemplo, um valor de retorno específico.
Um teste pode apenas provar a presença de um erro, nunca a ausência de todos os erros. Os testes colocam um limite superior na correção do programa.
Cobertura de código
Para definir métricas de cobertura de código, o código-fonte pode ser convertido em um gráfico de fluxo de controle em que cada nó contém um segmento linear do código. O controle flui entre esses nós apenas no final de cada bloco e é sempre condicional (se condição, então vá para o nó A, depois vá para o nó B). O gráfico possui um nó inicial e um nó final.
Portanto, geralmente é útil verificar a cobertura da condição .
true
efalse
. Isso implica cobertura total da agência, mas é bastante caro. O programa pode ter restrições adicionais que excluem determinadas combinações. Essa técnica é boa para obter cobertura de ramificação, pode encontrar código morto, mas não consegue encontrar erros decorrentes da condição incorreta .Ao construir a entrada de teste usando a cobertura da condição, o curto-circuito deve ser levado em consideração. Por exemplo,
precisa ser testado com
foo(false, whatever)
,foo(true, false)
efoo(true, true)
para a cobertura condição múltipla mínima completo.Se você possui objetos que podem estar em vários estados, o teste de todas as transições de estado análogas ao controle de fluxos parece sensato.
Existem algumas métricas de cobertura mais complexas, mas geralmente são semelhantes às métricas apresentadas aqui.
Estes são métodos de teste de caixa branca e podem ser parcialmente automatizados. Observe que um conjunto de testes de unidade deve ter uma cobertura de código alta por qualquer métrica escolhida, mas 100% nem sempre é possível. É especialmente difícil testar o tratamento de exceções, onde as falhas precisam ser injetadas em locais específicos.
Testes funcionais
Depois, há testes funcionais que afirmam que o código adere às especificações, visualizando a implementação como uma caixa preta. Esses testes são úteis para testes de unidade e testes de integração. Como é impossível testar com todos os dados de entrada possíveis (por exemplo, testar o comprimento da string com todas as strings possíveis), é útil agrupar a entrada (e a saída) em classes equivalentes - se
length("foo")
estiver correto,foo("bar")
provavelmente funcionará também. Para cada combinação possível entre as classes de equivalência de entrada e saída, pelo menos uma entrada representativa é escolhida e testada.Deve-se testar adicionalmente
length("")
,foo("x")
,length(longer_than_INT_MAX)
,length(null)
, elength("null byte in \x00 the middle")
...Com números, isso significa testar
0, ±1, ±x, MAX, MIN, ±∞, NaN
e com comparações de ponto flutuante testando dois carros alegóricos vizinhos. Como outra adição, valores de teste aleatórios podem ser selecionados nas classes de equivalência. Para facilitar a depuração, vale a pena registrar a semente usada…Testes não funcionais: testes de carga, testes de estresse
Um software possui requisitos não funcionais, que também precisam ser testados. Isso inclui testes nos limites definidos (testes de carga) e além deles (testes de estresse). Para um jogo de computador, isso pode estar afirmando um número mínimo de quadros por segundo em um teste de carga. Um site pode ser submetido a um teste de estresse para observar os tempos de resposta quando o dobro do número de visitantes antecipados está prejudicando os servidores. Esses testes não são relevantes apenas para sistemas inteiros, mas também para entidades únicas - como uma tabela de hash é degradada com um milhão de entradas?
Outros tipos de testes são testes de todo o sistema em que cenários são simulados ou testes de aceitação para provar que o contrato de desenvolvimento foi cumprido.
Métodos sem teste
Avaliações
Existem técnicas que não são de teste que podem ser usadas para garantir a qualidade. Exemplos são orientações, revisões formais de código ou programação de pares. Embora algumas peças possam ser automatizadas (por exemplo, usando linters), elas geralmente demandam muito tempo. No entanto, as revisões de código por programadores experientes têm uma alta taxa de descoberta de bugs e são especialmente valiosas durante o design, onde nenhum teste automatizado é possível.
Quando as revisões de código são tão boas, por que ainda escrevemos testes? A grande vantagem dos conjuntos de testes é que eles podem ser executados (principalmente) automaticamente e, portanto, são muito úteis para testes de regressão .
Verificação formal
A verificação formal confirma e prova certas propriedades do código. A verificação manual é principalmente viável para partes críticas, menos para programas inteiros. As provas colocam um limite inferior na correção do programa. As provas podem ser automatizadas até certo ponto, por exemplo, através de um verificador de tipo estático.
Certos invariantes podem ser verificados explicitamente usando
assert
instruções.Todas essas técnicas têm seu lugar e são complementares. O TDD grava os testes funcionais antecipadamente, mas os testes podem ser julgados por suas métricas de cobertura assim que o código for implementado.
Escrever código testável significa escrever pequenas unidades de código que podem ser testadas separadamente (funções auxiliares com granularidade adequada, princípio de responsabilidade única). Quanto menos argumentos cada função receber, melhor. Esse código também se presta à inserção de objetos simulados, por exemplo, via injeção de dependência.
fonte
Talvez o "teste baseado em especificação" também responda à sua pergunta. Verifique estes módulos de teste (que ainda não usei). Eles exigem que você escreva uma expressão matemática para especificar conjuntos de valores de teste, em vez de escrever um teste de unidade usando valores de dados únicos selecionados.
Teste :: Lectrotest
Como o autor diz, este Módulo Perl foi inspirado no Quick-Check Module de Haskell . Existem mais links nesta página, alguns dos quais estão mortos.
fonte
Uma abordagem matematicamente baseada é o teste de todos os pares . A idéia é que a maioria dos bugs seja ativada por uma única opção de configuração e a maior parte do restante seja ativada por um determinado par de opções tomadas simultaneamente. Assim, a maioria pode ser capturada testando "todos os pares". Uma explicação matemática (com generalizações) está aqui:
O sistema AETG: uma abordagem para testes com base no design combinatório
(existem muitas outras referências)
fonte
Existem algumas equações matemáticas usadas, mas isso depende do tipo de teste de software que você está usando. Por exemplo, a suposição crítica de falhas pressupõe que as falhas dificilmente são o produto de 2 ou mais falhas simultâneas. A seguinte equação é: f = 4n + 1. f = função que calcula o número de casos de teste para um determinado número de variáveis ( n) + 1 é a adição da constante em que todas as variáveis assumem o valor nominal.
Outro tipo de teste que requer equações matemáticas é o Teste de robustez, que está testando a robustez ou a correção dos casos de teste em um processo de teste. Nesse teste, você digitaria variáveis dentro do intervalo de entrada legítimo (casos de teste limpos) e variáveis de entrada fora do intervalo de entrada (casos de teste sujos). Você usaria a seguinte equação matemática: f = 6n + 1 . 6n indica que cada variável deve assumir 6 valores diferentes, enquanto os outros valores assumem o valor nominal. * + 1 * representa a adição da constante 1.
fonte