Prueba de mutación

Método de prueba de software

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 mutación implican modificar un programa en pequeñas formas. [1] Cada versión mutada se llama mutante y las pruebas detectan y rechazan mutantes haciendo que el comportamiento de la versión original sea diferente del mutante. Esto se llama matar al mutante. Las suites 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 probador 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 programas", en la que se modifica el programa. Una definición más general del análisis de mutaciones es el uso de 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 normalmente se aplica a las pruebas. Por tanto, las pruebas de mutaciones 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 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 dado, 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 cuidará a los guardias?"].) La idea detrás de las pruebas de mutación es que si se introduce un mutante, esto normalmente causa un error en la funcionalidad del programa que las pruebas deberían encontrar. De esta manera, se prueban las pruebas. Si un mutante no es detectado por el conjunto de pruebas, esto generalmente indica que el conjunto de pruebas no puede localizar los fallos representados por el mutante, pero también puede indicar que la mutación no introduce fallos, es decir, la mutación es un cambio válido que no afecta la funcionalidad. Una forma (común) en que un mutante puede ser válido es que el código que se ha cambiado es "código muerto" que nunca se ejecuta.

Para que las pruebas de mutación funcionen a gran escala, se suele introducir 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 costo de las pruebas de mutación había reducido su uso práctico como método de prueba de software. Sin embargo, el uso creciente de lenguajes de programación orientados a objetos y marcos de pruebas unitarias ha llevado a la creación de herramientas de prueba de mutación que prueban partes individuales de una aplicación.

Objetivos

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

  • Identificar fragmentos de código que han sido probados débilmente (aquellos cuyos mutantes no son eliminados) [1]
  • Identificar pruebas débiles (aquellas que nunca matan mutantes) [7]
  • calcular la puntuación de mutación, [4] la puntuación de mutación es el número de mutantes eliminados / número total de mutantes.
  • Aprenda sobre la propagación de errores y la infección de estado en el programa.

Historia

Las pruebas de mutación fueron propuestas originalmente por Richard Lipton cuando era estudiante en 1971, [8] y fueron desarrolladas y publicadas por primera vez por DeMillo, Lipton y Sayward. [1] La primera implementación de una herramienta de pruebas de mutación 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 un poder computacional masivo, 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 ) extendió muchos de los principios al dominio de la verificación de hardware. Mientras que el análisis de mutaciones solo espera detectar una diferencia en el resultado producido, Certess extiende esto al verificar que un verificador en el banco de pruebas realmente detecte 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 mutación. En el fuzzing, los mensajes o datos intercambiados dentro de las interfaces de comunicación (tanto dentro como entre instancias de software) se mutan para detectar fallas o diferencias en el procesamiento de los datos. Codenomicon [10] (2001) y Mu Dynamics (2005) desarrollaron los conceptos de fuzzing para una plataforma de prueba de mutación totalmente con estado, 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] La "cercanía" se pretende que se base en el comportamiento, no en la sintaxis. La segunda hipótesis se denomina efecto de acoplamiento . El efecto de acoplamiento afirma que los fallos simples pueden producirse en cascada o acoplarse para formar otros fallos emergentes. [11] [12]

Los mutantes de orden superior también revelan fallas sutiles e importantes, que refuerzan 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 de a uno por vez para cada fragmento aplicable del código fuente. El resultado de aplicar un operador de mutación al programa se denomina mutante . Si el conjunto de pruebas puede detectar el cambio (es decir, una de las pruebas falla), se dice que el mutante está eliminado .

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

si ( a && b ) { c = 1 ; } de lo contrario { c = 0 ; }            

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

si ( a || b ) { c = 1 ; } de lo contrario { 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 prueba deberían infectar el estado del programa al provocar estados de programa diferentes 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 fuerte 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 de código . Requiere mucho menos poder de cómputo 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 equivalente en comportamiento al original. Dichos 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 mutación. El esfuerzo necesario para comprobar si los mutantes son equivalentes o no puede ser muy alto incluso para programas pequeños. [17] Una revisión sistemática de la literatura de 2014 de una amplia gama de enfoques para superar el problema de mutantes equivalentes [18] identificó 17 técnicas relevantes (en 22 artículos) y tres categorías de técnicas: detección (DEM); sugerencia (SEM); y evitación de 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 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, supongamos que tiene dos mutantes, A y B, que 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 investigadores 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 desempeña 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:

  • Eliminación de la declaración
  • Duplicación o inserción de declaraciones, p. ej. goto fail;[20]
  • Reemplazo de subexpresiones booleanas con verdadero y falso
  • Reemplazo de algunas operaciones aritméticas por otras, por ejemplo +con *, -con/
  • Reemplazo de algunas relaciones booleanas con otras, por ejemplo >con >=, ==y<=
  • Reemplazo de variables por otras del mismo ámbito (los tipos de variables deben ser compatibles)
  • Eliminar el cuerpo del método. [21]

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] para objetos complejos como contenedores, [24] etc.

Tipos de operadores de mutación

Los operadores para contenedores se denominan operadores de mutación a nivel de clase . Los operadores a nivel de clase modifican la estructura del programa añadiendo, eliminando o modificando 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 Access Modifier Change, Type Cast Operator Insertion y Type Cast Operator Deletion. 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 de nivel de clase , MuJava también incluye operadores de mutación de nivel de método , denominados operadores tradicionales. Estos operadores tradicionales están diseñados en función de características que se encuentran comúnmente en los lenguajes procedimentales. Llevan a cabo cambios en las 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:

Declaración de mutación

La mutación de declaraciones es un proceso en el que se modifica intencionalmente un bloque de código, ya sea eliminando o copiando ciertas declaraciones. Además, permite reordenar las 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 deliberadamente 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 habituales. [27] La ​​mutación de declaraciones es como una herramienta de diagnóstico que proporciona información sobre la solidez y la resiliencia del código, lo que ayuda a los programadores a mejorar la calidad y la confiabilidad generales de su software.

Por ejemplo, en el fragmento de código a continuación, se elimina toda la sección 'else':

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 valores se produce cuando se ejecuta una modificación en los valores de los parámetros o de las constantes dentro del código. Esto suele implicar ajustar los valores sumando o restando 1, pero también puede implicar realizar cambios más sustanciales en los valores. Las modificaciones específicas que se realizan 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 más alto. 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 con un valor más pequeño. Esta prueba tiene como objetivo evaluar cómo el código maneja entradas más pequeñas. Asegurarse de que el código funciona 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: de valor pequeño a valor mayor function multiplicarPorDosMutation1 ( valor ) { valor de retorno * 10 ; }      // Mutación de valor: función de valor mayor a valor menor 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 la detección de fallas o debilidades en la lógica de toma de decisiones del programa. Este método implica alterar deliberadamente los 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, lo que evita 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 la eficacia de sus segmentos de código de toma de decisiones.

Por ejemplo:

// Función del código original isPositive ( número ) { return número > 0 ; }      // Mutación de decisión: Cambiar la función del operador de comparación isPositiveMutation1 ( number ) { return number >= 0 ; }      // Mutación de decisión: negar el resultado de la función isPositiveMutation2 ( number ) { return ! ( number > 0 ); }      

Marcos que implementan pruebas de mutación

  • Pitest - Marco de Java

Véase también

Referencias

  1. ^ abcd Richard A. DeMillo, Richard J. Lipton y Fred G. Sayward. Sugerencias para la selección de datos de prueba: ayuda para el programador en ejercicio. IEEE Computer, 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, consultado 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.S2CID62549502  .
  4. ^ abc Paul Ammann y Jeff Offutt. Introducción a las pruebas de software. Cambridge University Press, 2008.
  5. ^ Jia, Yue; Harman, Mark (septiembre de 2009). "An Analysis and Survey of the Development of Mutation Testing" (PDF) . CREST Centre, King's College London, Technical Report TR-09-06 . 37 (5): 649–678. doi :10.1109/TSE.2010.62. S2CID  6853229. Archivado desde el original (PDF) el 2017-12-04.
  6. ^ Dasso, Aristides; 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 la ampliación de un conjunto de pruebas automatizadas a través del análisis de mutaciones", 2008
  8. ^ ab Mutation 2000: Uniendo lo Ortogonal Archivado el 28 de septiembre de 2011 en Wayback Machine por A. Jefferson Offutt y Roland H. Untch.
  9. ^ Tim A. Budd, Análisis de mutaciones de datos de pruebas de programas. Tesis doctoral, Universidad de Yale, New Haven, Connecticut, 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 en las pruebas de software. ACM Trans. Softw. Eng. Methodol. 1, 1 (enero de 1992), 5-20.
  12. ^ AT Acree, TA Budd, RA DeMillo, RJ Lipton y FG Sayward, "Análisis de mutaciones", Instituto Tecnológico de Georgia, Atlanta, Georgia, Informe técnico GIT-ICS-79/08, 1979.
  13. ^ Yue Jia; Harman, M., "Construcción de fallas sutiles mediante pruebas de mutación de orden superior", Análisis y manipulación de código fuente, 2008 Octava conferencia internacional de trabajo del IEEE sobre , vol., n.º, págs. 249, 258, 28-29 de septiembre de 2008
  14. ^ Maryam Umar, "Una evaluación de operadores de mutación para mutantes equivalentes", tesis de maestría, 2006
  15. ^ Polo M. y Piattini M., "Pruebas de mutación: aspectos prácticos y análisis de costes", Universidad de Castilla-La Mancha (España), Presentación, 2009
  16. ^ Anderson S., "Pruebas de mutación", Universidad de Edimburgo, Facultad de Informática, Presentación, 2011
  17. ^ PG Frankl, SN Weiss y C. Hu. Pruebas de uso general versus pruebas de mutación: una comparación experimental de la efectividad. Journal of Systems and Software , 38:235–253, 1997.
  18. ^ Superando el problema del mutante equivalente: 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". Conferencia internacional sobre ingeniería y sistemas de información de gestión (ICEMIS) de 2016. 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}}: CS1 maint: bot: estado de URL original desconocido ( enlace )
  20. ^ Error SSL/TLS de Apple por Adam Langley.
  21. ^ Niedermayr, Rainer; Juergens, 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: Association for Computing Machinery. 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 SQL y errores de formato de cadenas por H. Shahriar y M. Zulkernine.
  26. ^ abcd Walters, Amy (1 de junio de 2023). "Comprensión de las pruebas de mutación: una guía completa". Herramienta de pruebas automatizadas 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 declaraciones". Sexta Conferencia Internacional sobre Pruebas, Verificación y Validación de Software del IEEE de 2013. 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 .
Obtenido de "https://es.wikipedia.org/w/index.php?title=Prueba_de_mutación&oldid=1251258105"