La generación de pruebas es el proceso de crear un conjunto de datos de prueba o casos de prueba para probar la idoneidad de aplicaciones de software nuevas o revisadas . Se considera que la generación de pruebas es un problema complejo y, aunque han surgido muchas soluciones, la mayoría de ellas se limitan a programas de juguetes. La generación de pruebas es un aspecto de las pruebas de software . Dado que las pruebas requieren mucha mano de obra y representan casi un tercio del costo del desarrollo del sistema, se considera importante el problema de generar datos de prueba de calidad de manera rápida, eficiente y precisa. [1]
Conceptos básicos
![](http://wikiimg.tojsiabtv.com/wikipedia/commons/thumb/7/7f/Control_flow_graph_of_function_with_two_if_else_statements.svg/220px-Control_flow_graph_of_function_with_two_if_else_statements.svg.png)
Modelo matematico
Un programa P podría considerarse como una función, P: S → R, donde S es el conjunto de todas las entradas posibles y R el conjunto de todas las salidas posibles. Una variable de entrada de la función P se asigna a un parámetro de entrada de P. P (x) denota la ejecución del programa para cierta entrada x. [1] [2]
Gráfico de flujo de control
Un gráfico de flujo de control de un programa P es un gráfico dirigido G = (N, E, s, e) que consta de un conjunto de nodos N y un conjunto de aristas E = {(n, m) | n, m ∈ N } conectando los nodos. [1] [2]
Cada nodo denota un bloque básico que en sí mismo es una secuencia de instrucciones. Es importante notar que en cada bloque básico el control ingresa por el nodo de entrada y sale al final sin detenerse ni ramificarse excepto al final. Básicamente, un bloque siempre se ejecuta como un todo. Los nodos de entrada y salida son dos nodos especiales indicados por sy e respectivamente.
Un borde en un gráfico de flujo de control representa una posible transferencia de control. Todos los bordes tienen asociada una condición o un predicado de rama. El predicado de rama podría ser el predicado vacío que siempre es verdadero. Para atravesar el borde, la condición del borde debe mantenerse. Si un nodo tiene más de un borde saliente, el nodo es una condición y los bordes se denominan ramas.
Un modelo
![](http://wikiimg.tojsiabtv.com/wikipedia/commons/thumb/f/f0/Model_-_Test_Data_Generation.png/220px-Model_-_Test_Data_Generation.png)
Un generador de datos de prueba sigue los siguientes pasos
- Construcción de gráfico de flujo de control de programa
- Selección de camino
- Generando datos de prueba
La base del generador es simple. El selector de ruta identifica las rutas. Una vez que se determina un conjunto de rutas de prueba, el generador de pruebas deriva datos de entrada para cada ruta que da como resultado la ejecución de la ruta seleccionada. Básicamente, nuestro objetivo es encontrar un conjunto de datos de entrada que recorra la ruta elegida por el selector de ruta. Esto se hace en dos pasos:
- Encuentra el predicado de la ruta para la ruta
- Resolver el predicado de la ruta
Idealmente, la solución será un sistema de ecuaciones que describa la naturaleza de los datos de entrada para recorrer el camino. En algunos casos, el generador proporciona al selector información sobre las rutas que son inviables, etc. [2]
Generadores de datos de prueba
Con base en el modelado matemático anterior, podemos simplemente plantear el problema del generador de datos de prueba como: Dado un programa P y una ruta u , genera la entrada x ∈ S, de modo que x atraviesa la ruta u .
Generadores de datos de prueba aleatorios
La generación aleatoria de datos de prueba es probablemente el método más simple para generar datos de prueba. La ventaja de esto es que se puede utilizar para generar entradas para cualquier tipo de programa. Por lo tanto, para generar datos de prueba, podemos generar aleatoriamente un flujo de bits y dejar que represente el tipo de datos necesarios. Sin embargo, la generación de datos de prueba aleatorios no genera datos de prueba de calidad, ya que no funciona bien en términos de cobertura . Dado que los datos generados se basan únicamente en la probabilidad, no pueden lograr una alta cobertura, ya que las posibilidades de encontrar fallas semánticamente pequeñas son bastante bajas. [3]
Si una falla solo es revelada por un pequeño porcentaje de la entrada del programa, se dice que es una falla semánticamente pequeña. Para ver un ejemplo de una falla semánticamente pequeña, considere el siguiente código:
prueba nula ( char x , char y ) { if ( x == y ) printf ( "Igual" ); else printf ( "No es igual" ); }
Es fácil ver que la probabilidad de ejecución de la primera declaración es significativamente menor que la de la segunda declaración. A medida que las estructuras en él se vuelven complejas, también lo hace la probabilidad de su ejecución. Por lo tanto, tales fallas semánticamente pequeñas son difíciles de encontrar usando la generación de datos de prueba aleatoria.
Sin embargo, la generación aleatoria de datos de prueba se utiliza generalmente como punto de referencia, ya que tiene la tasa más baja aceptable de generación de datos de prueba.
Generadores de datos de prueba orientados a objetivos
El enfoque orientado a objetivos proporciona una guía hacia un determinado conjunto de caminos. Los generadores de datos de prueba en este enfoque generan una entrada para cualquier ruta u en lugar del enfoque habitual de generar entradas desde la entrada hasta la salida de un bloque de código. Por lo tanto, el generador puede encontrar cualquier entrada para cualquier ruta p que sea un subconjunto de la ruta u . Esto reduce drásticamente el riesgo de generar rutas relativamente inviables y proporciona una forma de dirigir la búsqueda. Dos métodos siguen esta técnica:
- El enfoque del encadenamiento
- Enfoque orientado a la afirmación.
Enfoque de encadenamiento
El enfoque de encadenamiento es una extensión del enfoque orientado a objetivos. Se ve que la principal limitación de los métodos de generación de datos de prueba es que solo se utiliza el gráfico de flujo de control para generar los datos de prueba. Este conocimiento limitado puede dificultar nuestra selección. Por lo tanto, se ve que el enfoque orientado a la ruta por lo general tiene que generar una gran cantidad de rutas antes de encontrar la ruta "correcta". Esto se debe a que la selección de la ruta es ciega. [4] El enfoque de encadenamiento intenta identificar una cadena de nodos que son vitales para la ejecución del nodo objetivo. El enfoque de encadenamiento comienza con la ejecución de cualquier entrada x arbitraria. El programa de búsqueda, durante la ejecución de cada rama, decide si continuar la ejecución a través de esta rama o si se toma una rama alternativa porque la rama actual no conduce al nodo objetivo. Si se observa que el flujo de ejecución no es deseable, se utilizan algoritmos de búsqueda para encontrar automáticamente una nueva entrada para cambiar la ejecución del flujo. Sin embargo, si para este punto también el proceso de búsqueda no puede encontrar la entrada X para cambiar el flujo de ejecución, entonces el enfoque de encadenamiento intenta alterar el flujo en el nodo p debido a lo cual se puede ejecutar una rama alternativa en p. [4] [5]
Enfoque orientado a la afirmación
El enfoque orientado a afirmaciones es una extensión del enfoque de encadenamiento. En este enfoque se insertan afirmaciones, es decir, condiciones de restricción. Esto se puede hacer de forma manual o automática. Si el programa no se detiene en la ejecución, hay un error en el programa o en la aserción .
Por lo tanto, cuando se ejecuta una aserción, debe mantenerse. Supongamos que tenemos el siguiente código:
prueba nula ( int a ) { int b , c ; b = a - 1 ; aserción ( b ! = 0 ); c = ( 1 / b ); }
En el código anterior, el programa debe mantenerse en la declaración de aserción . Si la afirmación no se cumple, significa que el camino seguido conduce a un error. Por lo tanto, el objetivo de este enfoque es encontrar un camino hacia una afirmación que no se mantenga. La otra gran ventaja de este enfoque es que todos los demás métodos esperan que el valor de una ejecución de los datos de prueba generados se calcule a partir de alguna otra fuente que no sea el código. Sin embargo, en este enfoque no es necesario ya que el valor esperado se proporciona con la aserción.
Generadores de datos de prueba de ruta
La generación de datos de prueba de Pathwise se considera uno de los mejores enfoques para la generación de datos de prueba. Este enfoque no le da al generador la opción de seleccionar entre múltiples rutas, sino que solo le da una ruta específica para que funcione. De ahí el nombre Pathwise Test Data Generator. Por lo tanto, excepto por el hecho de que este método utiliza rutas específicas, es bastante similar a la generación de datos de prueba orientada a objetivos. El uso de caminos específicos conduce a un mejor conocimiento y predicción de la cobertura . Sin embargo, esto también dificulta la generación de los datos de prueba necesarios.
Los generadores de datos de prueba de ruta requieren dos entradas del usuario:
- El programa que se probará
- Criterio de prueba (por ejemplo: cobertura de ruta, cobertura de declaración, etc.)
Si los sistemas se basan únicamente en el gráfico de flujo de control para seleccionar rutas específicas, la mayoría de las veces conduce a la selección de rutas no factibles. En vista de esto, se han propuesto mecanismos para una generación de datos de prueba basada en restricciones. [6] Estos mecanismos se centran en las pruebas basadas en fallos que introducen cambios deliberados en el código. Estos cambios deliberados se denominan "mutantes" y este tipo de prueba se denomina Prueba de mutación .
Generadores de datos de prueba inteligentes
Los generadores de datos de prueba inteligentes dependen de un análisis sofisticado del código para guiar la búsqueda de los datos de prueba. Los generadores de datos de prueba inteligentes utilizan esencialmente uno de los métodos de generación de datos de prueba junto con el análisis detallado del código. Este enfoque puede generar datos de prueba más rápido que los otros enfoques, pero el análisis requerido para la utilización de este enfoque en una amplia variedad de programas es bastante complejo y requiere una gran cantidad de conocimientos para anticipar las diferentes situaciones que pueden surgir. [7] [8] Existen paquetes de código abierto para esto, como DataGenerator. [9]
Generadores de datos caóticos
Los generadores de datos caóticos generan datos a partir de un atractor caótico. El atractor caótico genera datos que no se repiten, y un pequeño cambio en las condiciones iniciales del atractor puede causar un gran cambio en los datos generados posteriormente. [10]
Generadores de hipermedia
Los generadores de hipermedia generan hipertexto, hipervolúmenes e hiperpelículas.
Generadores de datos cuánticos
Un generador de datos cuánticos genera qubits de acuerdo con algún otro generador de datos. En este caso, los qubits son los datos.
Generadores de afirmaciones de prueba
Una vez que tenemos algunos datos de prueba, se necesita un oráculo de prueba para evaluar el comportamiento del programa bajo prueba para la entrada de prueba dada. Hay diferentes tipos de oráculos que se pueden usar. [11]
También se pueden mejorar las afirmaciones de los datos de prueba existentes. Por ejemplo, se pueden generar y agregar nuevas aserciones en casos de prueba existentes. Esto es lo que hace el sistema DSpot en el contexto del lenguaje de programación Java: realiza un análisis dinámico de los casos de prueba JUnit y genera afirmaciones faltantes. [12]
Generadores de casos de prueba
Mientras que los generadores de datos de prueba solo generan entradas, los generadores de casos de prueba sintetizan casos de prueba completos. Por ejemplo, en un lenguaje orientado a objetos, un caso de prueba contiene creaciones de objetos y llamadas a métodos. Cuando se maximiza la cobertura, los casos de prueba generados no tienen afirmación.
// Ejemplo de caso de prueba generado que cubre el constructor y dos métodos. @Test void generateJunitTest () { Cuenta b = nueva cuenta bancaria ( 0 ); b . depósito ( 7 ); b . retirar ( 3 ); }
Para agregar aserciones en los casos de prueba generados, se debe tener un oráculo de prueba . Por ejemplo, se puede usar una implementación de referencia que tenga un oráculo. Entonces, el valor esperado de las aserciones generadas es el valor real devuelto por la implementación de referencia.
@Test void generateJunitTest () { Cuenta b = nueva cuenta bancaria ( 0 ); b . depósito ( 7 ); b . retirar ( 3 ); asertEquals ( 4 , b . getAmount ()); }
EvoSuite es un ejemplo de un generador de casos de prueba para Java.
Problemas de generación de pruebas
La generación de pruebas es muy compleja. El uso de la asignación de memoria dinámica en la mayor parte del código escrito en la industria es el problema más grave que enfrentan los generadores de datos de prueba, ya que el uso del software se vuelve altamente impredecible, debido a esto, se vuelve más difícil anticipar las rutas que el programa podría hacer. tomar, lo que hace que sea casi imposible para los generadores de datos de prueba generar datos de prueba exhaustivos. Sin embargo, en la última década se han logrado avances significativos para abordar mejor este problema mediante el uso de algoritmos genéticos y otros algoritmos de análisis. Las siguientes son áreas problemáticas que se encuentran al implementar las técnicas de generación de datos de prueba para el código utilizado en la industria real. [2]
Matrices y punteros
Se puede considerar que las matrices y punteros tienen construcciones similares y también sufren el mismo tipo de problemas. Las matrices y los punteros crean problemas durante la ejecución simbólica ya que complican la sustitución ya que no se conocen sus valores. Además, para generar entradas para matrices y punteros, existen múltiples factores de complicación como el índice de la matriz o la estructura de la entrada que se debe dar al puntero. Este problema se ve agravado por la posibilidad de asignación dinámica en matrices y punteros.
Objetos
Los objetos , debido a su naturaleza dinámica, plantean un problema para la generación de pruebas. Esto se ve agravado por el uso de otras características orientadas a objetos . Todo esto hace que sea difícil determinar qué código se llamará en tiempo de ejecución. Se ha intentado abordar el problema del código orientado a objetos mediante el uso de la mutación. [13]
Bucles
Los bucles que varían su comportamiento en función de las variables de entrada son potencialmente problemáticos, ya que es difícil anticipar el camino que podría tomarse. Sin embargo, si la ruta dada es específica y no cambia el comportamiento, los bucles no causan ningún problema. Se han sugerido algunas técnicas para resolver este problema potencial con los bucles. [14]
Módulos
Por lo general, un programa consta de módulos que, a su vez, constan de funciones. Se han propuesto dos soluciones para generar datos de prueba para tales funciones: [14]
- Solución de fuerza bruta: inserta las funciones llamadas en el objetivo
- Análisis de las funciones llamadas: analice primero las funciones llamadas y genere predicados de ruta para esas funciones.
Sin embargo, a menudo no se puede acceder al código fuente de los módulos y, por lo tanto, no siempre es posible realizar un análisis estático completo.
Caminos inviables
Generar datos de prueba para recorrer un camino implica resolver un sistema de ecuaciones. Si no hay soluciones, entonces el camino dado es inviable. Sin embargo, en esto estamos limitados por el problema de la naturaleza indecidible del sistema de ecuaciones. El método más común adoptado es establecer el mayor número de iteraciones antes de declarar la ruta como inviable.
Satisfacción de la restricción
Como sugiere el nombre, la satisfacción de restricciones es el proceso de encontrar una solución que se ajuste a un conjunto de restricciones que las variables deben satisfacer. Por tanto, una solución es un vector de variables que satisface todas las restricciones. La satisfacción de las restricciones es un problema difícil de resolver y, por lo tanto, no suele implementarse correctamente. Todos los programas deben satisfacer una restricción de una forma u otra. Ha habido muchos métodos como relajación iterativa, algoritmos genéticos , etc. que permiten resolver restricciones. [6] [7]
Legibilidad de las pruebas generadas
Uno de los desafíos de la generación de pruebas es la legibilidad: cuando las pruebas generadas están destinadas a comprometerse con el conjunto de pruebas, los desarrolladores han podido comprender fácilmente los casos de prueba generados. Sin embargo, los casos de prueba generados a menudo contienen nombres de variables generados poco conocidos. [15] Una forma de superar este problema es que, en lugar de generar nuevas pruebas, se mejoren las pruebas existentes ya escritas por humanos. Esto se conoce como amplificación de prueba. [dieciséis]
Ver también
- Pruebas de software
- Plan de prueba
- Banco de pruebas
- Datos de prueba
- Fuzzing
Referencias
- ↑ a b c Korel, Bogdan (agosto de 1990). "Generación automatizada de datos de prueba de software". Transacciones IEEE sobre ingeniería de software . 16 (8): 870–879. CiteSeerX 10.1.1.121.8803 . doi : 10.1109 / 32.57624 .
- ^ a b c d Edvardsson, Jon (octubre de 1999). "Una encuesta sobre la generación automática de datos de prueba". Actas de la Segunda Conferencia sobre Informática e Ingeniería en Linkoping . CiteSeerX 10.1.1.20.963 .
- ^ Offutt, J .; J. Hayes (1996). "Un modelo semántico de fallos del programa". Simposio Internacional de Pruebas y Análisis de Software . CiteSeerX 10.1.1.134.9338 .
- ^ a b Korel, Bogdan (1990). "Un enfoque dinámico de la generación automatizada de datos de prueba". Jornada de Mantenimiento de Software .
- ^ Ferguson, Roger; Bogdan Korel (1996). "El enfoque de encadenamiento para la generación de datos de prueba de software" (PDF) . ACM .
- ^ a b DeMillo, RA; Offutt AJ (septiembre de 1991). "Generación de datos de prueba automática basada en restricciones". Transacciones IEEE sobre ingeniería de software . 19 (6): 640. CiteSeerX 10.1.1.91.1702 . doi : 10.1109 / 32.232028 .
- ^ a b Pargas, Roy; Harrold, Mary; Peck, Robert (1999). "Generación de datos de prueba mediante algoritmos genéticos" (PDF) . Revista de pruebas, verificación y confiabilidad de software . 9 (4): 263-282. CiteSeerX 10.1.1.33.7219 . doi : 10.1002 / (sici) 1099-1689 (199912) 9: 4 <263 :: aid-stvr190> 3.0.co; 2-y .
- ^ Michael, CC; McGraw, GE; Schatz, MA; Walton, CC (1997). "Algoritmos genéticos para la generación dinámica de datos de prueba". Actas 12th IEEE International Conference Automated Software Engineering . págs. 307-308. CiteSeerX 10.1.1.50.3866 . doi : 10.1109 / ASE.1997.632858 . ISBN 978-0-8186-7961-2.
- ^ "El generador de datos" . finraos.github.io . Consultado el 1 de septiembre de 2019 .
- ^ "Generador de datos del caos" .
- ^ Barr, Earl T .; Harman, Mark; McMinn, Phil; Shahbaz, Muzammil; Yoo, Shin (1 de mayo de 2015). "El problema de Oracle en las pruebas de software: una encuesta" . Transacciones IEEE sobre ingeniería de software . 41 (5): 507–525. doi : 10.1109 / TSE.2014.2372785 . ISSN 0098-5589 .
- ^ Danglot, Benjamin; Vera-Pérez, Oscar Luis; Baudry, Benoit; Monperrus, Martín (2019). "Mejora automática de pruebas con DSpot: un estudio con diez proyectos maduros de código abierto" . Ingeniería de software empírica . 24 (4): 2603–2635. arXiv : 1811.08330 . doi : 10.1007 / s10664-019-09692-y . ISSN 1573-7616 .
- ^ Seater, Robert; Gregory Dennis. "Generación automatizada de datos de prueba con SAT" (PDF) . Cite journal requiere
|journal=
( ayuda ) - ^ a b Ramamoorthy, CV; SF Ho; WT Chen (diciembre de 1976). "Sobre la generación automatizada de datos de prueba del programa". Transacciones IEEE sobre ingeniería de software . SE-2 (4): 293–300. doi : 10.1109 / tse.1976.233835 .
- ^ Grano, Giovanni; Scalabrino, Simone; Gall, Harald C .; Oliveto, Rocco (2018). "Una investigación empírica sobre la legibilidad de casos de prueba manuales y generados" (PDF) . Actas de la 26ª Conferencia sobre Comprensión de Programas - ICPC '18 . págs. 348–351. doi : 10.1145 / 3196321.3196363 . ISBN 9781450357142.
- ^ Danglot, Benjamin; Vera-Pérez, Oscar; Yu, Zhongxing; Zaidman, Andy; Monperrus, Martin; Baudry, Benoit (2019). "Un estudio de literatura de bolas de nieve sobre la amplificación de la prueba". Revista de sistemas y software . 157 : 110398. arXiv : 1705.10692 . doi : 10.1016 / j.jss.2019.110398 .