stringtranslate.com

Pruebas de mutación

Las pruebas de mutación (o análisis de mutación o mutación de programa ) se utilizan para diseñar nuevas pruebas de software y evaluar la calidad de las pruebas de software existentes. Las pruebas de mutaciones implican modificar un programa en pequeñas formas. [1] Cada versión mutada se denomina mutante y las pruebas detectan y rechazan mutantes haciendo que el comportamiento de la versión original difiera del mutante. A esto se le llama matar al mutante. Los conjuntos de pruebas se miden por el porcentaje de mutantes que matan. Se pueden diseñar nuevas pruebas para matar mutantes adicionales. Los mutantes se basan en operadores de mutación bien definidos que imitan errores de programación típicos (como usar el operador o el nombre de variable incorrectos) o fuerzan la creación de pruebas valiosas (como dividir cada expresión por cero). El propósito es ayudar al evaluador a desarrollar pruebas efectivas o localizar debilidades en los datos de prueba utilizados para el programa o en secciones del código a las que rara vez o nunca se accede durante la ejecución . Las pruebas de mutación son una forma de prueba de caja blanca . [2] [3]

Introducción

La mayor parte de este artículo trata sobre la "mutación de programa", en la que se modifica el programa. Una definición más general de análisis de mutaciones es utilizar reglas bien definidas en estructuras sintácticas para realizar cambios sistemáticos en los artefactos de software. [4] El análisis de mutaciones se ha aplicado a otros problemas, pero generalmente se aplica a las pruebas. Por tanto, las pruebas de mutación se definen como el uso del análisis de mutaciones para diseñar nuevas pruebas de software o para evaluar pruebas de software existentes. [4] Por lo tanto, el análisis y las pruebas de mutaciones se pueden aplicar a modelos de diseño, especificaciones, bases de datos, pruebas, XML y otros tipos de artefactos de software, aunque la mutación de programas es la más común. [5]

Descripción general

Se pueden crear pruebas para verificar la corrección de la implementación de un sistema de software determinado, pero la creación de pruebas aún plantea la pregunta de si las pruebas son correctas y cubren suficientemente los requisitos que han originado la implementación. [6] (Este problema tecnológico es en sí mismo un ejemplo de un problema filosófico más profundo llamado " Quis custodiet ipsos custodes? " ["¿Quién protegerá a los guardias?"].) La idea detrás de las pruebas de mutación es que si se introduce un mutante, este normalmente causa un error en la funcionalidad del programa que las pruebas deberían encontrar. De esta manera, se prueban las pruebas. Si el conjunto de pruebas no detecta un mutante, esto normalmente indica que el conjunto de pruebas no puede localizar las fallas representadas por el mutante, pero también puede indicar que la mutación no introduce fallas, es decir, la mutación es un cambio válido. eso no afecta la funcionalidad. Una forma (común) en que un mutante puede ser válido es que el código que se ha modificado es un "código muerto" que nunca se ejecuta.

Para que las pruebas de mutaciones funcionen a escala, generalmente se introduce una gran cantidad de mutantes, lo que lleva a la compilación y ejecución de una cantidad extremadamente grande de copias del programa. Este problema del coste de las pruebas de mutaciones había reducido su uso práctico como método de prueba de software. Sin embargo, el mayor uso de lenguajes de programación orientados a objetos y marcos de prueba unitaria ha llevado a la creación de herramientas de prueba de mutaciones que prueban partes individuales de una aplicación.

Objetivos

Los objetivos de las pruebas de mutación son múltiples:

Historia

Las pruebas de mutación fueron propuestas originalmente por Richard Lipton cuando era estudiante en 1971, [8] y desarrolladas y publicadas por primera vez por DeMillo, Lipton y Sayward. [1] La primera implementación de una herramienta de prueba de mutaciones fue realizada por Timothy Budd como parte de su trabajo de doctorado (titulado Análisis de mutaciones ) en 1980 en la Universidad de Yale . [9]

Recientemente, con la disponibilidad de una potencia informática masiva, ha habido un resurgimiento del análisis de mutaciones dentro de la comunidad informática y se ha trabajado para definir métodos de aplicación de pruebas de mutaciones a lenguajes de programación orientados a objetos y lenguajes no procedimentales como XML . SMV y máquinas de estados finitos .

En 2004, una empresa llamada Certess Inc. (ahora parte de Synopsys ) amplió muchos de los principios al dominio de verificación de hardware. Mientras que el análisis de mutaciones sólo espera detectar una diferencia en el resultado producido, Certess amplía esto verificando que un verificador en el banco de pruebas realmente detectará la diferencia. Esta extensión significa que se evalúan las tres etapas de verificación, a saber: activación, propagación y detección. A esto lo llamaron calificación funcional.

El fuzzing puede considerarse un caso especial de prueba de mutaciones. En la fuzzing, los mensajes o datos intercambiados dentro de las interfaces de comunicación (tanto dentro como entre instancias de software) se modifican para detectar fallas o diferencias en el procesamiento de los datos. Codenomicon [10] (2001) y Mu Dynamics (2005) desarrollaron conceptos de fuzzing hasta convertirlos en una plataforma de prueba de mutaciones con estado completo, completa con monitores para ejercitar exhaustivamente las implementaciones de protocolos.

Descripción general de las pruebas de mutación

Las pruebas de mutación se basan en dos hipótesis. La primera es la hipótesis del programador competente . Esta hipótesis afirma que los programadores competentes escriben programas que están cerca de ser correctos. [1] "Cerrar" pretende basarse en el comportamiento, no en la sintaxis. La segunda hipótesis se llama efecto de acoplamiento . El efecto de acoplamiento afirma que las fallas simples pueden caer en cascada o acoplarse para formar otras fallas emergentes. [11] [12]

Los mutantes de orden superior también revelan fallas sutiles e importantes, que respaldan aún más el efecto de acoplamiento. [13] [14] [7] [15] [16] Los mutantes de orden superior se habilitan mediante la creación de mutantes con más de una mutación.

Las pruebas de mutación se realizan seleccionando un conjunto de operadores de mutación y luego aplicándolos al programa fuente uno a la vez para cada parte aplicable del código fuente. El resultado de aplicar un operador de mutación al programa se llama mutante . Si el conjunto de pruebas es capaz de detectar el cambio (es decir, una de las pruebas falla), entonces se dice que el mutante ha muerto .

Por ejemplo, considere el siguiente fragmento de código C++:

si ( a && b ) { c = 1 ; } más { c = 0 ; }            

El operador de mutación de condición reemplazaría &&y ||produciría el siguiente mutante:

si ( a || b ) { c = 1 ; } más { c = 0 ; }            

Ahora bien, para que la prueba mate a este mutante, se deben cumplir las siguientes tres condiciones:

  1. Una prueba debe llegar a la declaración mutada.
  2. Los datos de entrada de la prueba deberían infectar el estado del programa causando diferentes estados del programa para el programa mutante y el original. Por ejemplo, una prueba con a = 1y b = 0haría esto.
  3. El estado incorrecto del programa (el valor de 'c') debe propagarse a la salida del programa y ser verificado por la prueba.

Estas condiciones se denominan colectivamente modelo RIP . [8]

Las pruebas de mutación débil (o cobertura de mutación débil ) requieren que solo se cumplan la primera y la segunda condición. Las pruebas de mutación sólidas requieren que se cumplan las tres condiciones. La mutación fuerte es más poderosa, ya que garantiza que el conjunto de pruebas realmente pueda detectar los problemas. La mutación débil está estrechamente relacionada con los métodos de cobertura del código . Se requiere mucha menos potencia informática para garantizar que el conjunto de pruebas satisfaga las pruebas de mutación débil que las pruebas de mutación fuerte.

Sin embargo, hay casos en los que no es posible encontrar un caso de prueba que pueda matar a este mutante. El programa resultante es conductualmente equivalente al original. Estos mutantes se denominan mutantes equivalentes .

La detección de mutantes equivalentes es uno de los mayores obstáculos para el uso práctico de las pruebas de mutaciones. El esfuerzo necesario para comprobar si los mutantes son equivalentes o no puede ser muy elevado incluso para programas pequeños. [17] Una revisión sistemática de la literatura realizada en 2014 sobre una amplia gama de enfoques para superar el problema de los mutantes equivalentes [18] identificó 17 técnicas relevantes (en 22 artículos) y tres categorías de técnicas: detección (DEM); sugerir (SEM); y evitar la generación de mutantes equivalentes (AEMG). El experimento indicó que la mutación de orden superior en general y la estrategia JudyDiffOp en particular proporcionan un enfoque prometedor para el problema de los mutantes equivalentes.

Además de los mutantes equivalentes, existen mutantes subsumidos que son mutantes que existen en la misma ubicación del código fuente que otro mutante y se dice que están "subsumidos" por el otro mutante. Los mutantes subsumidos no son visibles para una herramienta de prueba de mutaciones y no contribuyen a las métricas de cobertura. Por ejemplo, digamos que tienes dos mutantes, A y B, y ambos cambian una línea de código de la misma manera. Primero se prueba el mutante A y el resultado es que el código no funciona correctamente. Luego se prueba el mutante B y el resultado es el mismo que con el mutante A. En este caso, se considera que el mutante B está subsumido por el mutante A, ya que el resultado de la prueba del mutante B es el mismo que el resultado de la prueba del mutante A. Por lo tanto, no es necesario probar el Mutante B, ya que el resultado será el mismo que el del Mutante A.

Operadores de mutación

Para realizar cambios sintácticos en un programa, un operador de mutación sirve como guía que sustituye partes del código fuente. Dado que las mutaciones dependen de estos operadores, los académicos han creado una colección de operadores de mutación para adaptarse a diferentes lenguajes de programación, como Java. La eficacia de estos operadores de mutación juega un papel fundamental en las pruebas de mutación. [19]

Los investigadores han explorado muchos operadores de mutación. A continuación se muestran algunos ejemplos de operadores de mutación para lenguajes imperativos:

Estos operadores de mutación también se denominan operadores de mutación tradicionales. También existen operadores de mutación para lenguajes orientados a objetos, [22] para construcciones concurrentes, [23] objetos complejos como contenedores, [24] etc.

Tipos de operadores de mutación

Los operadores de contenedores se denominan operadores de mutación a nivel de clase . Los operadores a nivel de clase alteran la estructura del programa agregando, eliminando o cambiando las expresiones que se examinan. Se han establecido operadores específicos para cada categoría de cambios. [19] Por ejemplo, la herramienta muJava ofrece varios operadores de mutación a nivel de clase, como cambio de modificador de acceso, inserción de operador de conversión de tipo y eliminación de operador de conversión de tipo. También se han desarrollado operadores de mutación para realizar pruebas de vulnerabilidad de seguridad de los programas. [25]

Además de los operadores a nivel de clase , MuJava también incluye operadores de mutación a nivel de método , conocidos como operadores tradicionales. Estos operadores tradicionales están diseñados en base a características que se encuentran comúnmente en los lenguajes procedimentales. Realizan cambios en declaraciones agregando, sustituyendo o eliminando operadores primitivos. Estos operadores se dividen en seis categorías: operadores aritméticos , operadores relacionales , operadores condicionales , operadores de desplazamiento , operadores lógicos y operadores de asignación . [19]

Tipos de pruebas de mutación

Hay tres tipos de pruebas de mutación;

Mutación de declaración

La mutación de declaraciones es un proceso en el que un bloque de código se modifica intencionalmente eliminando o copiando ciertas declaraciones. Además, permite reordenar declaraciones dentro del bloque de código para generar varias secuencias. [26] Esta técnica es crucial en las pruebas de software, ya que ayuda a identificar posibles debilidades o errores en el código. Al realizar cambios deliberados en el código y observar cómo se comporta, los desarrolladores pueden descubrir errores o fallas ocultas que podrían pasar desapercibidas durante las pruebas periódicas. [27] La ​​mutación de declaraciones es como una herramienta de diagnóstico que proporciona información sobre la solidez y resistencia del código, ayudando a los programadores a mejorar la calidad general y la confiabilidad de su software.

Por ejemplo, en el siguiente fragmento de código, se elimina toda la sección "otro":

función checkCredentials ( nombre de usuario , contraseña ) { if ( nombre de usuario === "admin" && contraseña === "contraseña" ) { return true ; } }                

Mutación de valor

La mutación de valor ocurre cuando se ejecuta una modificación en el parámetro y/o valores constantes dentro del código. Por lo general, esto implica ajustar los valores sumando o restando 1, pero también puede implicar realizar cambios más sustanciales en los valores. Las alteraciones específicas realizadas durante la mutación de valores incluyen dos escenarios principales:

En primer lugar, está la transformación de un valor pequeño a un valor superior. Esto implica reemplazar un valor pequeño en el código por uno más grande. El propósito de este cambio es evaluar cómo responde el código cuando encuentra entradas más grandes. Ayuda a garantizar que el código pueda procesar de manera precisa y eficiente estos valores más grandes sin encontrar errores o problemas inesperados. [26]

Por el contrario, el segundo escenario implica cambiar un valor más alto por uno más pequeño. En este caso, reemplazamos un valor más alto dentro del código por un valor más pequeño. Esta prueba tiene como objetivo evaluar cómo el código maneja entradas más pequeñas. Garantizar que el código funcione correctamente con valores más pequeños es esencial para evitar problemas o errores imprevistos al tratar con dichos datos de entrada. [26]

Por ejemplo:

// Función del código original multiplicarPorDos ( valor ) { valor de retorno * 2 ; }      // Mutación de valor: valor pequeño a valor mayor función multiplicarByTwoMutation1 ( valor ) { valor de retorno * 10 ; }      // Mutación de valor: valor más alto a valor más pequeño función multiplicarByTwoMutation2 ( valor ) { valor de retorno / 10 ; }      

Mutación de decisión

Las pruebas de mutación de decisiones se centran en la identificación de errores de diseño dentro del código, con especial énfasis en detectar fallas o debilidades en la lógica de toma de decisiones del programa. Este método implica alterar deliberadamente operadores aritméticos y lógicos para exponer posibles problemas. [26] Al manipular estos operadores, los desarrolladores pueden evaluar sistemáticamente cómo responde el código a diferentes escenarios de decisión. Este proceso ayuda a garantizar que las vías de toma de decisiones del programa sean sólidas y precisas, evitando errores costosos que podrían surgir de una lógica defectuosa. Las pruebas de mutación de decisiones sirven como una herramienta valiosa en el desarrollo de software, ya que permiten a los desarrolladores mejorar la confiabilidad y efectividad de sus segmentos de código de toma de decisiones.

Por ejemplo:

// La función del código original es Positiva ( número ) { número de retorno > 0 ; }      // Mutación de decisión: cambiar la función del operador de comparación isPositiveMutation1 ( número ) { return number >= 0 ; }      // Mutación de decisión: Negar el resultado función isPositiveMutation2 ( número ) { retorno ! ( número > 0 ); }      

Ver también

Referencias

  1. ^ abcd Richard A. DeMillo, Richard J. Lipton y Fred G. Sayward. Consejos para la selección de datos de prueba: ayuda para el programador practicante. Computadora IEEE, 11(4):34-41. Abril de 1978.
  2. ^ Ostrand, Thomas (2002), "Pruebas de caja blanca", Enciclopedia de ingeniería de software , Sociedad Estadounidense del Cáncer, doi :10.1002/0471028959.sof378, ISBN 978-0-471-02895-6, recuperado el 16 de marzo de 2021
  3. ^ Misra, S. (2003). "Evaluación de cuatro metodologías de cobertura de pruebas de caja blanca". CCECE 2003 - Conferencia Canadiense sobre Ingeniería Eléctrica e Informática. Hacia una tecnología solidaria y humana (Cat. No.03CH37436) . vol. 3. Montreal, Que., Canadá: IEEE. págs. 1739-1742. doi :10.1109/CCECE.2003.1226246. ISBN 978-0-7803-7781-3. S2CID  62549502.
  4. ^ a b C Paul Ammann y Jeff Offutt. Introducción a las pruebas de software. Prensa de la Universidad de Cambridge, 2008.
  5. ^ Jia, Yue; Harman, Mark (septiembre de 2009). "Un análisis y estudio del desarrollo de las pruebas de mutaciones" (PDF) . Centro CREST, King's College London, Informe técnico TR-09-06 . 37 (5): 649–678. doi :10.1109/TSE.2010.62. S2CID  6853229. Archivado desde el original (PDF) el 4 de diciembre de 2017.
  6. ^ Dasso, Arístides; Funes, Ana (2007). Verificación, Validación y Pruebas en Ingeniería de Software . Idea Group Inc. ISBN 978-1591408512.
  7. ^ ab Smith B., "Sobre cómo guiar el aumento de un conjunto de pruebas automatizadas mediante análisis de mutaciones", 2008
  8. ^ ab Mutación 2000: Uniendo lo ortogonal Archivado el 28 de septiembre de 2011 en la Wayback Machine por A. Jefferson Offutt y Roland H. Untch.
  9. ^ Tim A. Budd, Análisis de mutaciones de datos de prueba de programas. Tesis doctoral, Universidad de Yale New Haven CT, 1980.
  10. ^ Kaksonen, Rauli. Un método funcional para evaluar la seguridad de la implementación de protocolos (tesis de licenciatura). Espoo. 2001.
  11. ^ A. Jefferson Offutt. 1992. Investigaciones sobre el efecto de acoplamiento de las pruebas de software. Transmisión ACM. Software. Ing. Método. 1, 1 (enero de 1992), 5-20.
  12. ^ AT Acree, TA Budd, RA DeMillo, RJ Lipton y FG Sayward, "Mutation Analysis", Instituto de Tecnología de Georgia, Atlanta, Georgia, Informe técnico GIT-ICS-79/08, 1979.
  13. ^ Yue Jia; Harman, M., "Construyendo fallas sutiles usando pruebas de mutación de orden superior", Análisis y manipulación del código fuente, 2008 Octava conferencia de trabajo internacional del IEEE, vol., no., páginas 249,258, 28 y 29 de septiembre de 2008
  14. ^ Maryam Umar, "Una evaluación de operadores de mutaciones para mutantes equivalentes", tesis de maestría, 2006
  15. ^ Polo M. y Piattini M., "Mutation Testing: aspectos prácticos y análisis de costes", Universidad de Castilla-La Mancha (España), Presentación, 2009
  16. ^ Anderson S., "Mutation Testing", Universidad de Edimburgo, Facultad de Informática, Presentación, 2011
  17. ^ PG Frankl, SN Weiss y C. Hu. Todos los usos versus pruebas de mutación: una comparación experimental de efectividad. Revista de sistemas y software , 38:235–253, 1997.
  18. ^ Superar el problema de los mutantes equivalentes: una revisión sistemática de la literatura y un experimento comparativo de mutación de segundo orden por L. Madeyski, W. Orzeszyna, R. Torkar, M. Józala. Transacciones IEEE sobre ingeniería de software
  19. ^ abc Hamimoune, Soukaina; Falah, Bouchaib (24 de septiembre de 2016). "Técnicas de prueba de mutaciones: un estudio comparativo". 2016 Conferencia Internacional sobre Ingeniería y MIS (ICEMIS). págs. 1–9. doi :10.1109/ICEMIS.2016.7745368. ISBN 978-1-5090-5579-1. S2CID  24301702. Archivado desde el original el 19 de junio de 2018 . Consultado el 8 de octubre de 2023 .{{cite book}}: Mantenimiento CS1: bot: estado de la URL original desconocido ( enlace )
  20. ^ Error de SSL/TLS de Apple por Adam Langley.
  21. ^ Niedermayr, Rainer; Jürgens, Elmar; Wagner, Stefan (14 de mayo de 2016). "¿Mis pruebas me dirán si rompo este código?". Actas del taller internacional sobre evolución y entrega continua de software . CSED'16. Austin, Texas: Asociación de Maquinaria de Computación. págs. 23-29. arXiv : 1611.07163 . doi :10.1145/2896941.2896944. ISBN 978-1-4503-4157-8. S2CID  9213147.
  22. ^ MuJava: un sistema automatizado de mutación de clases Archivado el 11 de marzo de 2012 en Wayback Machine por Yu-Seung Ma, Jeff Offutt y Yong Rae Kwo.
  23. ^ Operadores de mutación para Java concurrente (J2SE 5.0) por Jeremy S. Bradbury, James R. Cordy, Juergen Dingel.
  24. ^ Mutación de objetos Java por Roger T. Alexander, James M. Bieman, Sudipto Ghosh, Bixia Ji.
  25. ^ Pruebas basadas en mutaciones de desbordamientos de búfer, inyecciones de SQL y errores de cadenas de formato por H. Shahriar y M. Zulkernine.
  26. ^ abcd Walters, Amy (1 de junio de 2023). "Comprensión de las pruebas de mutaciones: una guía completa". Herramienta de prueba automatizada basada en IA testRigor . Consultado el 8 de octubre de 2023 .
  27. ^ Deng, Lin; Offutt, Jeff; Li, Nan (22 de marzo de 2013). "Evaluación empírica del operador de mutación por eliminación de declaración". 2013 IEEE Sexta Conferencia Internacional sobre Pruebas, Verificación y Validación de Software. págs. 84–93. doi :10.1109/ICST.2013.20. ISBN 978-0-7695-4968-2. ISSN  2159-4848. S2CID  12866713 . Consultado el 8 de octubre de 2023 .