La prueba de mutación ( análisis de mutación o mutación de programa ) es un método de prueba de software que implica pequeños cambios en el código del programa. [1] Si un conjunto de pruebas no detecta dichos cambios, se considera insuficiente. Estos cambios se denominan mutaciones y se basan en declaraciones mutantes que imitan los errores comunes de los programadores (como usar el operador o el nombre de la variable incorrectos) o nos obligan a crear pruebas útiles.
La prueba de mutación fue propuesta por el estudiante Richard Lipton en 1971 [2] y fue desarrollada y publicada por primera vez por DeMille, Lipton y Seyward. La primera implementación de una herramienta de prueba de mutaciones fue creada por Timothy Budd de la Universidad de Yale en su disertación (titulada "Análisis mutacional") en 1980.
El método de prueba de mutaciones es computacionalmente costoso y no ha sido popular hasta hace poco. Recientemente, sin embargo, este método ha vuelto a atraer el interés de los investigadores en el campo de la informática.
Las pruebas de mutación consisten en seleccionar operadores de mutación y aplicarlos uno por uno a cada pieza del código fuente del programa. Un operador de mutación es una regla de transformación de código fuente. El resultado de una sola aplicación de un operador de mutación a un 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á muerto . Por ejemplo, considere el siguiente fragmento de código de un programa C++:
si ( a && b ) { c = 1 ; } más { c = 0 _ }El operador de mutación de condición se reemplazará &&con ||y creará el siguiente mutante:
si ( un || b ) { c = 1 ; } más { c = 0 _ }Para que la prueba mate a este mutante, se deben cumplir las siguientes condiciones:
Estas condiciones se conocen colectivamente como el modelo RIP .
Las pruebas de mutación débil (o cobertura de mutación débil ) solo requieren que se cumplan las dos primeras condiciones. Las pruebas de mutación fuerte requieren que se cumplan las tres condiciones y garantizan que el conjunto de pruebas pueda detectar el cambio. Las pruebas de mutaciones débiles están estrechamente relacionadas con las técnicas de cobertura de código . Comparar la prueba con las condiciones de una mutación débil requiere muchos menos cálculos que verificar las condiciones de una mutación fuerte.
Muchas declaraciones de mutación pueden conducir a programas equivalentes. Por ejemplo, considere el siguiente fragmento de programa:
índice int = 0 ; mientras ( ... ) { … ; índice ++ ; si ( índice == 10 ) { romper ; } }El operador de mutación de condición puede ser reemplazado ==por >=con obteniendo así el siguiente mutante:
índice int = 0 ; mientras ( ... ) { … ; índice ++ ; si ( índice >= 10 ) { romper ; } }Sin embargo, no existe ninguna prueba que pueda matar a este mutante. El programa resultante es equivalente al programa original. Estos mutantes se denominan mutantes equivalentes .
El reconocimiento de mutantes equivalentes es una de las mayores barreras para el uso de pruebas de mutación en la práctica. El esfuerzo para verificar si un mutante es equivalente puede ser muy grande incluso para programas pequeños. [3]
Se han explorado muchos tipos de operadores de mutación. Por ejemplo, para idiomas imperativos, se pueden usar los siguientes operadores:
Además, existen operadores para lenguajes orientados a objetos, [4] operadores para programación paralela, [5] operadores para estructuras de datos como contenedores [6] , etc.