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.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. 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 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 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 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) identificar testes fracos (aqueles que nunca matam mutantes)Smith B., "On Guiding Augmentation of an Automated Test Suite via Mutation Analysis," 2008 calcular uma pontuação de mutação Paul Ammann and Jeff Offutt. Introduction to Software Testing. Cambridge University Press, 2008. aprender sobre propagação de erro e infecção de estado no programa
História O teste de mutação foi originalmente proposto por Richard Lipton como em 1971,Mutation 2000: Uniting the Orthogonal by A. Jefferson Offutt and Roland H. Untch. e primeiro desenvolvido e publicado por DeMillo, Lipton e Sayward. 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.Tim A. Budd, Mutation Analysis of Program Test Data. PhD thesis, Yale University New Haven CT, 1980.
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 Kaksonen, Rauli. A Functional Method for Assessing Protocol Implementation Security (Licentiate thesis). Espoo. 2001. (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 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. 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.A. Jefferson Offutt. 1992. Investigations of the software testing coupling effect. ACM Trans. Softw. Eng. Methodol. 1, 1 (January 1992), 5-20.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.
Falhas sutis e importantes também são reveladas por mutantes de ordem superior, que suportam ainda mais o efeito de acoplamento.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. 2008Maryam Umar, "An Evaluation of Mutation Operators For Equivalent Mutants," MS Thesis, 2006Polo M. and Piattini M., "Mutation Testing: practical aspects and cost analysis," University of Castilla-La Mancha (Spain), Presentation, 2009Anderson S., "Mutation Testing", the University of Edinburgh, School of Informatics, Presentation, 2011 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:
Um teste deve alcançar a declaração mutada. 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. 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 .
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.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. Uma revisão sistemática da literatura de uma ampla gama de abordagens para superar o Problema Mutante Equivalente (apresentado por 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 ) 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 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;Apple's SSL/TLS bug by Adam Langley. 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,MuJava: An Automated Class Mutation System by Yu-Seung Ma, Jeff Offutt and Yong Rae Kwo. para construções concorrentes,Mutation Operators for Concurrent Java (J2SE 5.0) by Jeremy S. Bradbury, James R. Cordy, Juergen Dingel. objetos complexos como contêineres,Mutation of Java Objects by Roger T. Alexander, James M. Bieman, Sudipto Ghosh, Bixia Ji. 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 Mutation-based Testing of Buffer Overflows, SQL Injections, and Format String Bugs by H. Shahriar and M. Zulkernine.
Ferramentas de teste de mutação
Alloy: MuAlloy C/C++ llvm-mutate Frama-C plugin mull-project/mull mutate_cpp accmut MUSIC dextool SRCIROR MART C# stryker-mutator/stryker-net ComparetheMarket/fettle Testura.Mutation Clojure mutant Crystal crytic Elixir JordiPolo/mutation Erlang parsifal-47/muterl Go zimmski/go-mutesting Haskell mucheck rudymatela/fitspec Java/JVM hcoles/pitest metamutator Major JavaScript stryker-mutator/stryker PHP infection/infection Python sixty-north/cosmic-ray boxed/mutmut xmutant.py Ruby mbj/mutant backus/mutest Rust llogiq/mutagen Scala sugakandrey/scalamu stryker4s Smalltalk pavel-krivanek/mutalk Swift SeanROlszewski/muter
Ver também
Teste de sanidade
Leitura adicional
See Ch. VII Test-Case Mutation for overview on mutation testing. See Ch. V Syntax Testing for an overview of mutation testing.
Categoria:Teste de software