Articulo de referencia

Pruebas de mutación

Las pruebas de mutación (o análisis de mutación o mutación de programas ) se utilizan para diseñar nuevas pruebas de software y evaluar la calidad de las pruebas de software exi...

Las pruebas de mutación (o análisis de mutación o mutación de programas ) 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 realizar pequeños cambios en el programa que se está probando. [ 1 ] Cada versión modificada se denomina mutante . Una prueba detecta, y por lo tanto rechaza, un mutante cuando falla; el fallo indica que la prueba discernió correctamente que el comportamiento del mutante difiere del comportamiento del código original. El rechazo se denomina eliminación del mutante. El valor de un conjunto de pruebas se mide por el porcentaje de mutantes que elimina. El conjunto de pruebas se puede mejorar añadiendo nuevas pruebas diseñadas para eliminar mutantes adicionales.

La creación de mutantes se realiza mediante operadores de mutación bien definidos que imitan errores de programación típicos (como usar un operador o nombre de variable incorrecto) o fuerzan la creación de pruebas valiosas (como dividir cada expresión por cero).

Las pruebas de mutación son una forma de pruebas de caja blanca . [ 2 ] [ 3 ] Su propósito es ayudar al probador a desarrollar pruebas de regresión efectivas al localizar debilidades en los datos de prueba utilizados para probar el programa y descubrir secciones del código del programa probado que rara vez o nunca se acceden durante la ejecución .

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 de análisis de mutación es el uso de reglas bien definidas sobre estructuras sintácticas para realizar cambios sistemáticos en los artefactos de software. [ 4 ] El análisis de mutación se ha aplicado a otros problemas, pero generalmente se aplica a las pruebas. Por lo tanto, las pruebas de mutación se definen como el uso del análisis de mutación para diseñar nuevas pruebas de software o para evaluar las pruebas de software existentes. [ 4 ] Así, el análisis de mutación y las pruebas se pueden aplicar al diseño de modelos, 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 cuestión de si estas son correctas y cubren suficientemente los requisitos asociados con 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 vigilará a los vigilantes?"]).

La idea detrás de las pruebas de mutación es que el programa que se está probando funcione según lo previsto, por lo que si se introduce una mutación y la funcionalidad cambia, esto significa que se introduce un error, que las pruebas deberían detectar. De esta manera, se prueban las funciones. Si la suite de pruebas no detecta una mutación, esto generalmente indica que la suite de pruebas no puede localizar los fallos representados por la mutación, pero también puede indicar que la mutación no introduce ningún fallo. Es decir, la mutación es un cambio válido, que produce el resultado deseado o que no afecta la funcionalidad. Una forma (común) en que una mutación puede ser válida es que el código modificado sea "código muerto" que nunca se ejecuta.

Para que las pruebas de mutación funcionen a gran escala, se suele introducir un gran número de mutantes, lo que conlleva la compilación y ejecución de muchísimas copias del programa. Este problema del elevado coste de las pruebas de mutación había reducido su uso práctico como método de prueba de software. Sin embargo, el creciente uso de lenguajes de programación orientados a objetos y marcos de pruebas unitarias ha propiciado la creación de herramientas de pruebas de mutación que permiten probar partes específicas de una aplicación.

Objetivos

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

  • identificar fragmentos de código poco probados (aquellos para los que no se eliminan los mutantes) [ 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.
  • Aprende sobre la propagación de errores y la infección de estados en el programa.

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 pruebas de mutación fue realizada por Timothy Budd como parte de su trabajo de doctorado (titulado Análisis de mutación ) en 1980 en la Universidad de Yale . [ 9 ]

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

En 2004, la empresa Certess Inc. (ahora parte de Synopsys ) extendió muchos de estos principios al ámbito de la verificación de hardware. Mientras que el análisis de mutaciones solo busca detectar diferencias en el resultado, Certess va más allá al verificar que un comprobador en el banco de pruebas detecte realmente dicha diferencia. Esta extensión implica que se evalúan las tres etapas de la verificación: activación, propagación y detección. A esto lo denominaron cualificació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 modifican para detectar fallos o diferencias en el procesamiento de los datos. Codenomicon [ 10 ] (2001) y Mu Dynamics (2005) desarrollaron los conceptos de fuzzing hasta convertirlos en una plataforma de prueba de mutación con estado completo, que incluye monitores para evaluar 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 ] El término "cerca" se refiere al comportamiento, no a la sintaxis. La segunda hipótesis se denomina efecto de acoplamiento . El efecto de acoplamiento afirma que las fallas simples pueden encadenarse o acoplarse para formar otras fallas emergentes. [ 11 ] [ 12 ]

Los mutantes de orden superior también revelan fallas sutiles e importantes, lo que respalda aún más el efecto de acoplamiento. [ 13 ] [ 14 ] [ 7 ] [ 15 ] [ 16 ] Los mutantes de orden superior se posibilitan 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 aplicándolos al programa fuente uno por uno para cada fragmento de código aplicable. El resultado de aplicar un operador de mutación al programa se denomina mutante . Si el conjunto de pruebas detecta el cambio (es decir, si una de las pruebas falla), se dice que el mutante ha sido eliminado .

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

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

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

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

Ahora bien, para que la prueba elimine a este mutante, deben cumplirse las siguientes tres condiciones:

  1. Una prueba debe llegar a la instrucción modificada.
  2. Los datos de entrada de prueba deberían infectar el estado del programa provocando diferentes estados del programa para el mutante y el programa 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 potente, ya que garantiza que el conjunto de pruebas pueda detectar los problemas de forma efectiva. La mutación débil está estrechamente relacionada con los métodos de cobertura de código . Requiere mucha menos potencia de cálculo para garantizar que el conjunto de pruebas cumpla con las pruebas de mutación débil que con las de mutación fuerte.

Sin embargo, existen casos en los que no es posible encontrar un caso de prueba que pueda eliminar este mutante. El programa resultante es conductualmente equivalente 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 elevado, incluso para programas pequeños. [ 17 ] Una revisión sistemática de la literatura de 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); 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 los mutantes equivalentes.

Además de los mutantes equivalentes, existen mutantes subsumidos , que son mutantes que se encuentran en la misma ubicación del código fuente que otro mutante y se dice que están "subsumidos" por este último. 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 tenemos dos mutantes, A y B, que modifican 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, el mutante B se considera subsumido por el mutante A, ya que el resultado de la prueba del mutante B es el mismo que el 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 para sustituir 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 numerosos operadores de mutación. A continuación, se presentan algunos ejemplos de operadores de mutación para lenguajes imperativos:

  • Eliminación de declaraciones
  • Duplicación o inserción de sentencias, por ejemplo goto fail;[ 20 ].
  • Sustitución de subexpresiones booleanas por verdadero y falso.
  • Sustitución de algunas operaciones aritméticas por otras, por ejemplo , +con *, -con/
  • Sustitución de algunas relaciones booleanas por otras, por ejemplo , >con y>===<=
  • Sustitución 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 ] 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 . Estos operadores alteran 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 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 en programas. [ 25 ]

Además de los operadores a nivel de clase , MuJava también incluye operadores de mutación a nivel de método , denominados operadores tradicionales. Estos operadores tradicionales están diseñados en base a características comunes en lenguajes procedimentales. Realizan cambios en las sentencias mediante la adición, sustitución o eliminación de 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

Existen tres tipos de pruebas de mutación;

Mutación de declaración

La mutación de sentencias es un proceso en el que un bloque de código se modifica intencionalmente eliminando o copiando ciertas sentencias. Además, permite reordenar las sentencias dentro del bloque de código para generar diversas 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 su comportamiento, los desarrolladores pueden descubrir errores o fallos ocultos que podrían pasar desapercibidos durante las pruebas regulares. [ 27 ] La mutación de sentencias es como una herramienta de diagnóstico que proporciona información sobre la robustez y la resiliencia del código, lo que ayuda a los programadores a mejorar la calidad y la fiabilidad generales de su software.

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

función checkCredentials ( nombre de usuario , contraseña ) { si ( nombre de usuario === "admin" && contraseña === "password" ) { devolver verdadero ; } }

Mutación de valor

La mutación de valores ocurre cuando se modifican los valores de parámetros o constantes dentro del código. Esto generalmente 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 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 uno mayor. Esto implica reemplazar un valor pequeño en el código por uno mayor. El propósito de este cambio es evaluar cómo responde el código cuando encuentra entradas mayores. Ayuda a garantizar que el código pueda procesar estos valores mayores de forma precisa y eficiente sin encontrar errores ni problemas inesperados. [ 26 ]

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

Por ejemplo:

// Código original función multiplyByTwo ( valor ) { return valor * 2 ; }// Mutación de valor: Valor pequeño a valor mayor function multiplyByTwoMutation1 ( value ) { return value * 10 ; }// Mutación de valor: Función de valor mayor a valor menor multiplyByTwoMutation2 ( valor ) { return valor / 10 ; }

Mutación de decisión

Las pruebas de mutación de decisiones se centran en la identificación de errores de diseño en el código, con especial énfasis en la detección de fallos o debilidades en la lógica de toma de decisiones del programa. Este método implica la alteración deliberada de 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 rutas de toma de decisiones del programa sean robustas y precisas, previniendo errores costosos que podrían surgir de una lógica defectuosa. Las pruebas de mutación de decisiones constituyen una valiosa herramienta en el desarrollo de software, permitiendo a los desarrolladores mejorar la fiabilidad y la eficacia de sus segmentos de código de toma de decisiones.

Por ejemplo:

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

Véase también

Referencias

  1. 1 2 3 4 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 Americana 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 de Ingeniería Eléctrica e Informática. Hacia una tecnología solidaria y humana (Cat. No. 03CH37436) . Vol. 3. Montreal, Que., Canadá: IEEE. pp. 1739–1742 . doi : 10.1109/CCECE.2003.1226246 . ISBN   978-0-7803-7781-3. S2CID 62549502 . 
  4. 1 2 3 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álisis y revisión del desarrollo de las pruebas de mutación" (PDF) . IEEE Transactions on Software Engineering . 37 (5): 649– 678. doi : 10.1109/TSE.2010.62 . S2CID 6853229. Archivado del original (PDF) el 4 de diciembre de 2017. 
  6. Dasso, Aristides; Funes, Ana (2007). Verificación, validación y pruebas en ingeniería de software . Idea Group Inc. ISBN 978-1591408512.
  7. 1 2 Smith B., "Sobre la guía para la ampliación de un conjunto de pruebas automatizadas mediante análisis de mutación", 2008
  8. 1 2 Mutación 2000: Uniendo lo ortogonal Archivado el 28-09-2011 en Wayback Machine por A. Jefferson Offutt y Roland H. Untch.
  9. Tim A. Budd, Análisis de mutaciones en datos de pruebas 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 del 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 fallos sutiles mediante pruebas de mutación de orden superior", Análisis y manipulación de código fuente, Octava Conferencia Internacional de Trabajo IEEE de 2008, 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, Escuela de Informática, Presentación, 2011
  17. PG Frankl, SN Weiss y C. Hu. Pruebas de todos los usos frente a 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. IEEE Transactions on Software Engineering
  19. 1 2 3 Hamimoune, Soukaina; Falah, Bouchaib (24 de septiembre de 2016). "Técnicas de prueba de mutación: Un estudio comparativo". Conferencia Internacional de Ingeniería y Sistemas de Información Gerencial (ICEMIS) de 2016. págs. 1–9 . doi : 10.1109/ICEMIS.2016.7745368 . ISBN  978-1-5090-5579-1. S2CID 24301702 . 
  20. Error de SSL/TLS de Apple por Adam Langley.
  21. Niedermayr, Rainer; Juergens, Elmar; Wagner, Stefan (14 de mayo de 2016). "¿Me avisarán mis pruebas 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 para detectar desbordamientos de búfer, inyecciones SQL y errores de formato de cadena, por H. Shahriar y M. Zulkernine.
  26. 1 2 3 4 Walters, Amy (2023-06-01). "Understanding Mutation Testing: A Comprehensive Guide" . testRigor AI-Based Automated Testing Tool . Recuperado el 2023-10-08 .
  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 sentencias". Sexta Conferencia Internacional IEEE de 2013 sobre Pruebas, Verificación y Validación de Software . págs. 84–93 . doi : 10.1109/ICST.2013.20 . ISBN  978-0-7695-4968-2ISSN 2159-4848 . S2CID 12866713 .​