Teste de mutação

Origem: Wikipédia, a enciclopédia livre.

O teste de mutação (ou análise de mutação ou mutação de programa) é um sistema usado para projetar novos testes de software e avaliar a qualidade dos testes de software existentes. O teste de mutação envolve a modificação de um programa em pequenas maneiras.[1] Cada versão mutada é chamada de mutante e os testes detectam e rejeitam mutantes, fazendo com que o comportamento da versão original seja diferente do mutante. Isso é chamado de matar o mutante. O objetivo é ajudar o testador a desenvolver testes eficazes ou localizar pontos fracos nos dados de teste usados para o programa ou em seções do código que raramente ou nunca são acessadas durante a execução. O teste de mutação é uma forma de teste de caixa-branca .

Introdução[editar | editar código-fonte]

O teste de mutação é definido como o uso de análise de mutação para projetar novos testes de software ou para avaliar os testes de software existentes. Assim, a análise e o teste de mutação podem ser aplicados a modelos de design, especificações, bancos de dados, testes, XML e outros tipos de artefatos de software, embora a mutação de programa seja a mais comum.

Visão geral[editar | editar código-fonte]

Testes podem ser criados para verificar a exatidão da implementação de um determinado sistema de software, mas a criação de testes ainda coloca a questão se os testes estão corretos e cobrem suficientemente os requisitos que originaram a implementação. (Quis custodiet ipsos custodes? A ideia é que se um mutante for introduzido sem ser detectado pelo conjunto de testes, isso indica que o código que foi mutado nunca foi executado (código morto) ou que o conjunto de testes foi incapaz de localizar as falhas representadas pelo mutante .

Pare funcionar, um grande número de mutantes são introduzidos, o que leva a execução de um número extremamente grande de cópias do programa, o que pode ser pesado. No entanto, o aumento do uso de linguagens de programação orientadas a objetos e estruturas de teste de unidade levou à criação de ferramentas de teste de mutação que testam partes individuais de um aplicativo.

Metas[editar | editar código-fonte]

Os objetivos do teste de mutação são múltiplos:

  • identificar fragmentos de código fracamente testados (aqueles para os quais os mutantes não são mortos) [1]
  • identificar testes fracos (aqueles que nunca matam mutantes)[2]
  • calcular uma pontuação de mutação [3]
  • aprender sobre propagação de erro e infecção de estado no programa [4]

História[editar | editar código-fonte]

O teste de mutação foi originalmente proposto por Richard Lipton como em 1971,[5] e primeiro desenvolvido e publicado por DeMillo, Lipton e Sayward.[1] A primeira implementação de uma ferramenta de teste de mutação foi feita por Timothy Budd como parte de seu trabalho de doutorado (intitulado Mutation Analysis) em 1980, na Universidade Yale.[6]

Recentemente, com o aumento da disponibilidade de grande poder de computação, houve um ressurgimento da análise de mutação dentro da comunidade da ciência da computação.

O fuzzing pode ser considerada um caso especial de teste de mutação. No fuzzing, as mensagens ou dados trocados dentro das interfaces de comunicação (tanto dentro quanto entre as instâncias do software) são transformados para detectar falhas ou diferenças no processamento dos dados. Codenomicon [7] (2001) e Mu Dynamics (2005) desenvolveram conceitos de fuzzing para uma plataforma de teste de mutação totalmente stateful, completa com monitores para exercitar completamente as implementações de protocolo.

Visão geral do teste de mutação[editar | editar código-fonte]

O teste de mutação é baseado em duas hipóteses. A primeira é a hipótese do programador competente . Esta hipótese afirma que a maioria das falhas de software introduzidas por programadores experientes são devidas a pequenos erros sintáticos.[1] A segunda hipótese é chamada de efeito de acoplamento . O efeito de acoplamento afirma que falhas simples podem formar uma cascata ou se acoplar para formar outras falhas emergentes.[8][9]

Falhas sutis e importantes também são reveladas por mutantes de ordem superior, que suportam ainda mais o efeito de acoplamento.[2][10][11][12][13] Mutantes de ordem superior são ativados pela criação de mutantes com mais de uma mutação.

O teste de mutação é feito selecionando um conjunto de operadores de mutação e aplicando-os ao programa de origem, um de cada vez, para cada parte aplicável do código-fonte. O resultado da aplicação de um operador de mutação ao programa é chamado de mutante . Se o conjunto de testes for capaz de detectar a mudança (ou seja, um dos testes falhar), o mutante é considerado morto .

Por exemplo, considere o seguinte fragmento de código C ++:

if (a && b) {
  c = 1;
} else {
  c = 0;
}

O operador de mutação de condição substituiria && por || e produzir o seguinte mutante:

if (a || b) {
  c = 1;
} else {
  c = 0;
}

Agora, para o teste matar esse mutante, as três condições a seguir devem ser atendidas:

  1. Um teste deve alcançar a declaração mutada.
  2. Os dados de entrada de teste devem infectar o estado do programa, causando diferentes estados do programa para o programa mutante e original. Por exemplo, um teste com a = 1 e b = 0 faria isso.
  3. O estado incorreto do programa (o valor de 'c') deve se propagar para a saída do programa e ser verificado pelo teste.

Essas condições são chamadas coletivamente de modelo RIP .[5]

O teste de mutação fraca (ou cobertura de mutação fraca ) requer que apenas a primeira e a segunda condições sejam satisfeitas. O teste de mutação forte requer que todas as três condições sejam satisfeitas. A mutação forte é mais poderosa, pois garante que o conjunto de testes possa realmente detectar os problemas. A mutação fraca está intimamente a cobertura de código.

No entanto, há casos em que não é possível encontrar um caso de teste que possa matar esse mutante. O programa resultante é comportamentalmente equivalente ao original. Esses mutantes são chamados de mutantes equivalentes .

A detecção de mutantes equivalentes é um dos maiores obstáculos para o uso prático de testes de mutação. O esforço necessário para verificar se os mutantes são equivalentes ou não pode ser muito alto, mesmo para programas pequenos.[14] Uma revisão sistemática da literatura de uma ampla gama de abordagens para superar o Problema Mutante Equivalente (apresentado por [15] ) identificou 17 técnicas relevantes (em 22 artigos) e três categorias de técnicas: detecção (DEM); sugerindo (SEM); e evitando a geração de mutantes equivalentes (AEMG). O experimento indicou que a mutação de ordem superior em geral e a estratégia JudyDiffOp em particular fornecem uma abordagem promissora para o problema mutante equivalente.

Operadores de mutação[editar | editar código-fonte]

Muitos operadores de mutação foram explorados por pesquisadores. Aqui estão alguns exemplos de operadores de mutação para linguagens imperativas:

  • Exclusão de extrato
  • Duplicação ou inserção de instrução, por exemplo, goto fail;[16]
  • Substituição de subexpressões booleanas por verdadeiro e falso
  • Substituição de algumas operações aritméticas por outras, por exemplo, + com *, - com /
  • Substituição de algumas relações booleanas por outras, por exemplo, > por >=, == e <=
  • Substituição de variáveis por outras do mesmo escopo (os tipos de variáveis devem ser compatíveis)

pontuação de mutação = número de mutantes mortos / número total de mutantes

Esses operadores de mutação também são chamados de operadores de mutação tradicionais. Existem também operadores de mutação para linguagens orientadas a objetos,[17] para construções concorrentes,[18] objetos complexos como contêineres,[19] etc. Os operadores de contêineres são chamados de operadores de mutação em nível de classe . Por exemplo, a ferramenta muJava oferece vários operadores de mutação em nível de classe, como Alteração do Modificador de Acesso, Inserção de Operador de Tipo e Exclusão de Operador de Tipo. Os operadores de mutação também foram desenvolvidos para realizar testes de vulnerabilidade de segurança de programas [20]

Ferramentas de teste de mutação[editar | editar código-fonte]

Ver também[editar | editar código-fonte]

Leitura adicional[editar | editar código-fonte]

Referências

  1. a b c d Richard A. DeMillo, Richard J. Lipton, and Fred G. Sayward. Hints on test data selection: Help for the practicing programmer. IEEE Computer, 11(4):34-41. April 1978.
  2. a b Smith B., "On Guiding Augmentation of an Automated Test Suite via Mutation Analysis," 2008
  3. Paul Ammann and Jeff Offutt. Introduction to Software Testing. Cambridge University Press, 2008.
  4. Musco, Vincenzo; Monperrus, Martin; Preux, Philippe (2016). «Mutation-Based Graph Inference for Fault Localization». doi:10.1109/SCAM.2016.24 
  5. a b Mutation 2000: Uniting the Orthogonal by A. Jefferson Offutt and Roland H. Untch.
  6. Tim A. Budd, Mutation Analysis of Program Test Data. PhD thesis, Yale University New Haven CT, 1980.
  7. Kaksonen, Rauli. A Functional Method for Assessing Protocol Implementation Security (Licentiate thesis). Espoo. 2001.
  8. A. Jefferson Offutt. 1992. Investigations of the software testing coupling effect. ACM Trans. Softw. Eng. Methodol. 1, 1 (January 1992), 5-20.
  9. A. T. Acree, T. A. Budd, R. A. DeMillo, R. J. Lipton, and F. G. Sayward, "Mutation Analysis," Georgia Institute of Technology, Atlanta, Georgia, Technique Report GIT-ICS-79/08, 1979.
  10. Yue Jia; Harman, M., "Constructing Subtle Faults Using Higher Order Mutation Testing," Source Code Analysis and Manipulation, 2008 Eighth IEEE International Working Conference on, vol., no., pp.249,258, 28-29 Sept. 2008
  11. Maryam Umar, "An Evaluation of Mutation Operators For Equivalent Mutants," MS Thesis, 2006
  12. Polo M. and Piattini M., "Mutation Testing: practical aspects and cost analysis," University of Castilla-La Mancha (Spain), Presentation, 2009
  13. Anderson S., "Mutation Testing", the University of Edinburgh, School of Informatics, Presentation, 2011
  14. P. G. Frankl, S. N. Weiss, and C. Hu. All-uses versus mutation testing: An experimental comparison of effectiveness. Journal of Systems and Software, 38:235–253, 1997.
  15. Overcoming the Equivalent Mutant Problem: A Systematic Literature Review and a Comparative Experiment of Second Order Mutation by L. Madeyski, W. Orzeszyna, R. Torkar, M. Józala. IEEE Transactions on Software Engineering
  16. Apple's SSL/TLS bug by Adam Langley.
  17. MuJava: An Automated Class Mutation System by Yu-Seung Ma, Jeff Offutt and Yong Rae Kwo.
  18. Mutation Operators for Concurrent Java (J2SE 5.0) by Jeremy S. Bradbury, James R. Cordy, Juergen Dingel.
  19. Mutation of Java Objects by Roger T. Alexander, James M. Bieman, Sudipto Ghosh, Bixia Ji.
  20. Mutation-based Testing of Buffer Overflows, SQL Injections, and Format String Bugs by H. Shahriar and M. Zulkernine.