¿Qué son las pruebas de mutación? (Ejemplo)

⚡ Resumen inteligente

Las pruebas de mutación introducen deliberadamente pequeños fallos en el código fuente y, a continuación, ejecutan el conjunto de pruebas existente contra cada versión defectuosa, midiendo si esas pruebas son lo suficientemente sólidas como para detectar el cambio.

  • 🔘 Definición: Un programa mutante es aquel que lleva un cambio sintáctico deliberado, y su eliminación demuestra que una prueba detectó dicho cambio.
  • ☑️ Proceso: Generar mutantes, ejecutar el conjunto de pruebas contra el original y el mutante, comparar los resultados y, a continuación, reforzar las pruebas que no detectaron fallos.
  • Operatores: OperaLa sustitución, la modificación de la expresión y la modificación de la declaración producen las tres familias principales de mutantes.
  • 🧪 puntuación: La puntuación de mutación es el porcentaje de mutantes eliminados y mide la fuerza de la afirmación en lugar de la mera ejecución de la línea.
  • 🛠️ Estampación: Stryker cubre JavaTexto, TypeScript, C# y Scala, mientras que PIT modifica el código de bytes de la JVM dentro de Maven y Gradle construcciones
  • ⚠️ Costo: Cada mutación vuelve a ejecutar todo el conjunto de pruebas, por lo que las pruebas de mutación son lentas, costosas y poco prácticas sin automatización.

Prueba de mutación

¿Qué son las pruebas de mutación?

Prueba de mutación Es un tipo de prueba de software en la que se modifican ciertas instrucciones del código fuente para comprobar si los casos de prueba son capaces de encontrar errores en dicho código. El objetivo de las pruebas de mutación es garantizar la calidad de los casos de prueba en términos de robustez, de modo que fallen ante el código fuente modificado.

El cambio realizado en un programa mutante debe mantenerse extremadamente pequeño, de modo que no afecte el objetivo general del programa. Las pruebas de mutación también se denominan estrategia de pruebas basada en fallos, porque implican crear deliberadamente un fallo en el programa. Es una forma de Blanco Box Pruebas que se aplica principalmente durante Examen de la unidad.

Las pruebas de mutación fueron propuestas en 1971 en un trabajo estudiantil por Richard Lipton y formalizadas en el artículo de 1978 "Sugerencias sobre la selección de datos de prueba" por DeMillo, Lipton y Sayward. Perdió impulso debido al costo computacional de la época y desde entonces ha recuperado terreno para lenguajes como Java, C#, Python, JavaScript y XML.

¿Cómo ejecutar las pruebas de mutación?

A continuación se detallan los pasos para realizar pruebas de mutación, también conocidas como análisis de mutación:

Paso 1: Se introducen fallos en el código fuente del programa mediante la creación de múltiples versiones llamadas mutantes. Cada mutante debe contener un único fallo, y el objetivo es provocar que la versión mutante falle, lo que demuestra la eficacia de los casos de prueba.

Paso 2: Los casos de prueba se aplican al programa original y también al programa mutante. Caso de prueba debe ser adecuado y se modifica para detectar fallas en un programa.

Paso 3: Compara los resultados del programa original y del programa modificado.

Paso 4: Si el programa original y el programa mutante generan resultados diferentes, el mutante se desactiva mediante el caso de prueba. Por lo tanto, el caso de prueba es suficientemente bueno para detectar el cambio entre el programa original y el mutante.

Paso 5: Si el programa original y el programa mutante generan el mismo resultado, el mutante se mantiene activo. En tales casos, es necesario crear casos de prueba más eficaces que eliminen todos los mutantes.

El diagrama a continuación tracson los mismos cinco pasos, desde el programa original, pasando por la generación de mutantes, hasta el veredicto de muerte o supervivencia.

Flujo de trabajo de las pruebas de mutación que muestra el programa original, los mutantes generados, la ejecución de la prueba y el veredicto de mutante muerto o vivo.

¿Cómo crear programas mutantes?

Una mutación no es más que un único cambio sintáctico en una instrucción del programa. Cada programa mutado debe diferir del programa original en exactamente una mutación.

programa original Programa mutante
Si (x>y)
Imprimir "Hola"
otro
Imprimir "Hola"
Si (x
Imprimir "Hola"
otro
Imprimir "Hola"

En el par anterior, solo cambió el operador de comparación, pero un caso de prueba donde x es mayor que y ahora imprime "Hi" en lugar de "Hello". La ilustración muestra esa única modificación sintáctica.

Un cambio sintáctico aplicado a una instrucción de programa para producir un único mutante

¿Qué cambiar en un Programa Mutante?

Existen diversas técnicas que pueden utilizarse para generar programas mutantes. Las tres familias que se describen a continuación abarcan la mayoría de los operadores de mutación que incluyen las herramientas.

Operaoperadores de reemplazo nd Operadores de modificación de expresiones Operadores de modificación de sentencias
Reemplazar el operando por otro operando (x por y, o y por x) o por un valor constante. Reemplazar un operador, o insertar un nuevo operador, en una instrucción de programa. Las declaraciones programáticas se modifican para crear programas mutantes.
Ejemplo:
Si (x>y) reemplaza los valores x e y
Si(5>y) reemplaza x por la constante 5
Ejemplo:
Si(x==y)
Podemos reemplazar == con >= y tener el programa mutante como
If(x>=y) e insertando ++ en la declaración
Si(x==++y)
Ejemplo:
Eliminar la parte else en una declaración if-else
Elimine toda la instrucción if-else para comprobar cómo se comporta el programa.

Algunos ejemplos de operadores de mutación:

  • Reemplazo de etiqueta IR a
  • Reemplazo de declaración de devolución
  • Eliminación de declaraciones
  • Inserción de operadores unarios (como – y ++)
  • Reemplazo del conector lógico
  • Reemplazo de nombre de matriz comparable
  • Eliminar la parte "else" de una instrucción if-else.
  • Agregar o reemplazar operadores
  • Reemplazo de declaración cambiando los datos.
  • Modificación de datos para las variables
  • Modificación de tipos de datos en el programa.

OperaLos tors que tocan una condición de contorno sobreviven con mayor frecuencia, por lo que los resultados de la mutación frecuentemente apuntan a brechas en análisis de valor límite.

Tipos de pruebas de mutación

In Ingeniería de SoftwareLas pruebas de mutación se clasifican fundamentalmente en tres tipos: mutación de sentencias, mutación de valores y mutación de decisiones.

  • Mutación de declaración – Se corta, pega o elimina una instrucción, por lo que el resultado puede ser la eliminación de algunas líneas de código.
  • Mutación de valor – Se modifican los valores de los parámetros y constantes principales, por ejemplo, cambiando un límite de bucle o un umbral.
  • Mutación de decisión – Las instrucciones de control se modifican, por ejemplo flipping un operador relacional o la negación de una condición.

Las herramientas agrupan sus operadores bajo estos tres encabezados, por lo que la familia que produjo un mutante superviviente le indica al probador qué tipo de aserción falta. Un mutante de decisión superviviente generalmente marca una rama no probada, que se superpone con prueba de bucle.

Automatización de pruebas de mutaciones

Las pruebas de mutación son extremadamente laboriosas y complicadas de realizar manualmente, por lo que se recomienda utilizar herramientas de automatización, que además reducen los costos. Una herramienta de mutación recopila los mutantes, programa las ejecuciones, registra qué mutante eliminó cada prueba fallida e informa el resultado.

Lista de herramientas disponibles:

  • Stryker — un marco de pruebas de mutación de código abierto con ediciones para JavaGuión y TypeScript (StrykerJS), C# y .NET (Stryker.NET) y Scala (Stryker4s).
  • PIT, también escrito PITest — un sistema de prueba de mutaciones para Java y la JVM que modifica el código de bytes compilado y se integra con Maven y Gradle construye junto a JUnit.

Ambos se ejecutan como un paso de compilación, por lo que pertenecen al mismo. integración continua oleoducto como el resto del pruebas de automatización sucesivamente.

Puntuación de mutación

La puntuación de mutación se define como el porcentaje de mutantes muertos con respecto al número total de mutantes.

Puntuación de mutación = (Mutantes asesinados / Número total de mutantes) * 100

La fórmula se muestra a continuación en el formato en que la mayoría de las herramientas la presentan.

Fórmula de puntuación de mutación: dividir los mutantes muertos por el número total de mutantes y multiplicar por cien.

Los casos de prueba se describen como mutación adecuada cuando la puntuación alcanza el 100 por ciento. En la práctica, el denominador debe excluir mutantes equivalentes — mutantes cuya sintaxis modificada se comporta exactamente igual que la original, por lo que ninguna prueba puede eliminarlos. Por lo tanto, las herramientas informan sobre los mutantes eliminados divididos por los mutantes eliminados más los mutantes no equivalentes supervivientes, y permiten que el probador marque los equivalentes.

Los resultados experimentales han demostrado que las pruebas de mutación son una forma eficaz de medir la idoneidad de los casos de prueba. El principal inconveniente es el coste de generar los mutantes y ejecutar cada caso de prueba contra cada uno de ellos.

Pruebas de mutación vs. Code Global

Alto prueba de cobertura Esto no demuestra la solidez de las pruebas. La cobertura de líneas y ramas registra qué instrucciones se ejecutaron, no si se verificó algo posteriormente, por lo que una prueba que llama a un método y no afirma nada sigue considerándose cubierta. Las pruebas de mutación solucionan este problema, ya que un mutante solo muere cuando una aserción falla realmente.

Aspecto Code cobertura Puntuación de mutación
que mide ¿Qué líneas o ramas ejecutaron las pruebas? ¿Qué fallos inyectados detectaron las pruebas?
Sensible a las afirmaciones No, una prueba sin aserciones también añade cobertura. Sí, un mutante sobrevive cuando ninguna afirmación falla.
Coste de una carrera Una prueba instrumentada Una prueba por mutante superviviente, hasta ahora más lento
Uso típico Una puerta rápida en cada compromiso Una revisión periódica más exhaustiva de los módulos críticos.
Modo de fallo Cobertura del 100 por ciento sin verificación real. Mutantes equivalentes que nunca pueden ser eliminados

Las dos métricas son complementarias. La cobertura nombra el código que nunca se alcanzó; la puntuación de mutación nombra el código alcanzado que nunca se comprobó. Ambas alimentan la misma proceso de gestión de defectosjunto con medidas como densidad de defectos.

Ventajas de las pruebas de mutación

Las siguientes son las ventajas de las pruebas de mutación:

  • Es un método eficaz para lograr una alta cobertura del programa fuente.
  • Prueba el conjunto de pruebas en sí mismo, lo que ningún otro técnica de prueba de software lo hace directamente.
  • Las pruebas de mutación proporcionan un buen nivel de detección de errores al desarrollador de software.
  • Este método descubre ambigüedades en el código fuente y tiene la capacidad de revelar fallos que las ejecuciones ordinarias nunca detectan.
  • Los mutantes supervivientes son susceptibles de acción: cada uno nombra una línea específica y un cambio específico que el conjunto de pruebas no detectó.
  • Los clientes se benefician de estas pruebas al recibir un sistema más fiable y estable.

Desventajas de las pruebas de mutación

Por otro lado, las siguientes son las desventajas de las pruebas de mutación:

  • Las pruebas de mutación son extremadamente costosas y requieren mucho tiempo, ya que es necesario generar y compilar una gran cantidad de programas mutantes.
  • Dado que requiere mucho tiempo, se puede afirmar que estas pruebas no se pueden realizar sin una herramienta de automatización.
  • Cada mutante se somete al mismo número de casos de prueba que el programa original, por lo que es necesario ejecutar una gran cantidad de mutantes contra todo el conjunto de pruebas.
  • Los mutantes equivalentes no pueden ser eliminados mediante ninguna prueba, y separarlos de los supervivientes genuinos generalmente requiere una revisión manual.
  • Debido a que el método modifica el código fuente, no es aplicable a Negro Box Pruebas.

¿Cuándo utilizar las pruebas de mutación?

El perfil de costos anterior implica que las pruebas de mutación rara vez se ejecutan en todo el código fuente con cada confirmación. Se amortizan cuando un fallo no detectado es costoso y el código bajo prueba es lo suficientemente pequeño como para mutar rápidamente.

  • Lógica crítica para la seguridad o lógica financiera — cálculo de pagos, normas fiscales y comprobaciones de autorización, donde una respuesta errónea silenciosa puede ser peor que un fallo del sistema.
  • Suites con cobertura sospechosamente alta — cuando la cobertura se acerca al 100 por ciento, pero aún así se escapan algunos defectos.
  • Se está refactorizando el código heredado. — Los resultados de las mutaciones revelan si las pruebas existentes detectarían una regresión.
  • Bibliotecas y componentes compartidos — un fallo en un reutilizado componente se multiplica entre todas las personas que llaman.
  • Equipos practicando desarrollo basado en pruebas — La puntuación comprueba que las pruebas escritas previamente realizan un trabajo real.

Por lo general, no vale la pena trabajar con prototipos desechables, con código generado o con poco código auxiliar sin lógica de ramificación, o con conjuntos de pruebas dominados por la lentitud. pruebas de integración que ya requieren horas para una sola pasada.

Por lo tanto, la mayoría de los equipos limitan la ejecución a los archivos modificados, establecen un umbral para los módulos que importan y dejan que el resto de los equipos... pruebas de regresión La suite lleva el resto de la ciclo de vida de las pruebas de software.

Preguntas Frecuentes

Un mutante equivalente es un cambio que altera la sintaxis pero no el comportamiento, como reemplazar un límite de bucle que nunca se alcanza. Ninguna prueba puede eliminarlo, por lo que debe marcarse y excluirse antes de que se confíe en la puntuación.

No existe una cifra universal. Los equipos suelen establecer un umbral alto para módulos críticos, como la lógica de pagos o seguridad, y uno más bajo para otros. Perseguir un porcentaje es menos útil que revisar cada mutación persistente en el código de alto riesgo.

Estas son las dos premisas en las que se basa la técnica. La primera afirma que los programadores escriben código casi correcto, por lo que los fallos reales son pequeños. La segunda indica que las pruebas que detectan fallos pequeños también detectan los fallos complejos derivados de ellos.

Restringir la mutación a los archivos modificados en la rama actual, reutilizar los datos de cobertura para que solo se ejecuten las pruebas que afecten a un mutante, ejecutar los mutantes en paralelo y hacer que la compilación falle si la puntuación disminuye en lugar de por una cifra absoluta.

Los modelos de aprendizaje automático predicen qué mutantes tienen probabilidades de sobrevivir para que la ejecución pueda recortarse, clasifican los mutantes equivalentes probables para su revisión y generan mutantes que se asemejan a fallos observados en el historial del proyecto en lugar de intercambios uniformes de operadores.

Sí, en lo que respecta a la parte mecánica. Dado un mutante superviviente y el método que se está probando, Copilot elabora la aserción o prueba de caso límite que falta. Un revisor aún debe confirmar que el valor esperado es correcto y no simplemente una copia del comportamiento actual.

Se audita el resultado. El desarrollo guiado por pruebas genera pruebas antes que el código, pero nada garantiza que esas pruebas sean suficientemente rigurosas. Una mutación periódica ejecutada en los mismos módulos muestra si el ciclo rojo-verde produjo pruebas que realmente fallan ante una respuesta incorrecta.

No. La inyección de fallos corrompe el entorno de ejecución y prueba fuzz alimenta entradas mal formadas, ambos juzgando la aplicación. Las pruebas de mutación cambian el código fuente y juzgan el conjunto de pruebas, por lo que el objeto bajo evaluación es diferente.

Resumir este post con: