Pruebas de mutación

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 21 de agosto de 2017; las comprobaciones requieren 7 ediciones .

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.

Reseña histórica

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.

Descripción general de las pruebas de mutación

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:

  • La prueba debe alcanzar (Reach) la declaración mutada .
  • Los datos de entrada de la prueba deben conducir a diferentes estados del programa mutante (Infectar) y el programa original. Por ejemplo, una prueba con a = 1y b = 0daría como resultado esto.
  • El valor de la variable cdebe afectar (Propagar) la salida del programa y ser verificado por la prueba.

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.

Mutantes equivalentes

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]

Operadores de mutación

Se han explorado muchos tipos de operadores de mutación. Por ejemplo, para idiomas imperativos, se pueden usar los siguientes operadores:

  • Borrar declaración de programa.
  • Reemplace cada expresión lógica con una constante lógica "verdadero" o "falso".
  • Sustituye cada operación aritmética por otra. Por ejemplo, +en *, -o /.
  • Reemplace cada operación lógica por otra. Por ejemplo, >en >=, ==o <=.
  • Reemplace cada variable con otra (del mismo alcance ). Las variables deben tener los mismos tipos.

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.

Notas

  1. A Practical System for Mutation Testing: Help for the Common Programmer Archivado el 14 de febrero de 2012 en Wayback Machine por A. Jefferson Offutt.
  2. Mutation 2000: Uniendo lo ortogonal . Fecha de acceso: 28 de enero de 2012. Archivado desde el original el 28 de septiembre de 2011.
  3. PG Frankl, SN Weiss y C. Hu. Todos los usos versus pruebas de mutación: una comparación experimental de efectividad. Diario de Sistemas y Software , 38:235-253, 1997.
  4. MuJava: An Automated Class Mutation System Archivado el 11 de marzo de 2012 en Wayback Machine por Yu-Seung Ma, Jeff Offutt y Yong Rae Kwo.
  5. Operadores de mutación para Java concurrente (J2SE 5.0) Archivado el 5 de febrero de 2012 en Wayback Machine por Jeremy S. Bradbury, James R. Cordy, Juergen Dingel.
  6. Mutation of Java Objects Archivado el 12 de mayo de 2013 en Wayback Machine por Roger T. Alexander, James M. Bieman, Sudipto Ghosh, Bixia Ji.