Las pruebas aleatorias son una técnica de prueba de software de caja negra en la que los programas se prueban generando entradas aleatorias e independientes. Los resultados de la salida se comparan con las especificaciones del software para verificar que la salida de la prueba sea correcta o incorrecta. [1] En caso de ausencia de especificaciones, se utilizan las excepciones del lenguaje, lo que significa que si surge una excepción durante la ejecución de la prueba, significa que hay una falla en el programa; también se utiliza como una forma de evitar pruebas sesgadas.
Historia de las pruebas aleatorias
Las pruebas aleatorias de hardware fueron examinadas por primera vez por Melvin Breuer en 1971 y el esfuerzo inicial para evaluar su efectividad fue realizado por Pratima y Vishwani Agrawal en 1975. [2]
En el ámbito del software, Duran y Ntafos habían examinado las pruebas aleatorias en 1984. [3]
Howden describió el uso de pruebas de hipótesis como base teórica para las pruebas aleatorias en Functional Testing and Analysis . El libro también contenía el desarrollo de una fórmula simple para estimar la cantidad de pruebas n que se necesitan para tener una confianza de al menos 1-1/ n en una tasa de fallas no mayor que 1/n. La fórmula es el límite inferior n log n , que indica la gran cantidad de pruebas sin fallas necesarias para tener una confianza incluso modesta en un límite de tasa de fallas modesto. [4]
Descripción general
Considere la siguiente función C++:
int myAbs ( int x ) { if ( x > 0 ) { return x ; } else { return x ; // error: debería ser '-x' } }
Ahora, las pruebas aleatorias para esta función podrían ser {123, 36, -35, 48, 0}. Solo el valor '-35' activa el error. Si no hay una implementación de referencia para verificar el resultado, el error podría pasar desapercibido. Sin embargo, se podría agregar una afirmación para verificar los resultados, como:
void testAbs ( int n ) { for ( int i = 0 ; i < n ; i ++ ) { int x = getRandomInput (); int resultado = myAbs ( x ); assert ( resultado >= 0 ); } }
La implementación de referencia está disponible a veces, por ejemplo, cuando se implementa un algoritmo simple de una manera mucho más compleja para lograr un mejor rendimiento. Por ejemplo, para probar una implementación del algoritmo de Schönhage–Strassen , se puede utilizar la operación estándar "*" en números enteros:
int obtenerRandomInput () { // … } void testFastMultiplication ( int n ) { for ( int i = 0 ; i < n ; i ++ ) { long x = getRandomInput (); long y = getRandomInput (); long resultado = fastMultiplication ( x , y ); assert ( x * y == resultado ); } }
Si bien este ejemplo se limita a tipos simples (para los cuales se puede utilizar un generador aleatorio simple), las herramientas que apuntan a lenguajes orientados a objetos generalmente exploran el programa para probar y encontrar generadores (constructores o métodos que devuelven objetos de ese tipo) y los invocan utilizando entradas aleatorias (ya sea generadas de la misma manera o generadas utilizando un generador pseudoaleatorio si es posible). Estos enfoques luego mantienen un grupo de objetos generados aleatoriamente y utilizan una probabilidad para reutilizar un objeto generado o crear uno nuevo. [5]
Sobre la aleatoriedad
Según el artículo seminal sobre pruebas aleatorias de D. Hamlet
[...] El significado técnico y matemático de "prueba aleatoria" se refiere a una falta explícita de "sistema" en la elección de los datos de prueba, de modo que no existe correlación entre las diferentes pruebas. [1]
Fortalezas y debilidades
Las pruebas aleatorias se elogian por los siguientes puntos fuertes:
- Es barato de usar: no es necesario ser inteligente respecto al programa bajo prueba.
- No tiene ningún sesgo: a diferencia de las pruebas manuales, no pasa por alto errores porque haya una confianza mal depositada en algún código.
- Es rápido encontrar candidatos con errores: normalmente lleva un par de minutos realizar una sesión de prueba.
- Si el software está correctamente especificado: encuentra errores reales.
Se han descrito las siguientes debilidades:
- Sólo encuentra errores básicos (por ejemplo, desreferenciación de puntero nulo ).
- Es tan preciso como lo sea la especificación, y las especificaciones suelen ser imprecisas.
- Se compara pobremente con otras técnicas para encontrar errores (por ejemplo, el análisis de programas estáticos ).
- Si se seleccionan aleatoriamente diferentes entradas en cada ejecución de prueba, esto puede crear problemas para la integración continua porque las mismas pruebas pasarán o fallarán aleatoriamente. [6]
- Algunos sostienen que sería mejor cubrir cuidadosamente todos los casos relevantes con pruebas construidas manualmente en un estilo de caja blanca, que confiar en la aleatoriedad. [6]
- Puede requerirse una gran cantidad de pruebas para niveles modestos de confianza en tasas de fallas modestas. Por ejemplo, se requerirán 459 pruebas sin fallas para tener al menos un 99% de confianza en que la probabilidad de falla es menor a 1/100. [4]
Tipos de pruebas aleatorias
Con respecto a la entrada
- Generación de secuencia de entrada aleatoria (es decir, una secuencia de llamadas a métodos)
- Secuencia aleatoria de entradas de datos (a veces llamada prueba estocástica), por ejemplo, una secuencia aleatoria de llamadas a métodos
- Selección aleatoria de datos de una base de datos existente
Guiado vs. no guiado
- Generación de pruebas aleatorias no dirigidas, sin heurísticas que guíen su búsqueda.
- Generación de pruebas aleatorias dirigidas, por ejemplo, "generación de pruebas aleatorias dirigidas por retroalimentación" [7] y "pruebas aleatorias adaptativas" [8]
Implementaciones
Algunas herramientas que implementan pruebas aleatorias:
- QuickCheck : una famosa herramienta de prueba, desarrollada originalmente para Haskell pero adaptada a muchos otros lenguajes, que genera secuencias aleatorias de llamadas API basadas en un modelo y verifica las propiedades del sistema que deberían mantenerse verdaderas después de cada ejecución.
- Randoop: genera secuencias de métodos e invocaciones de constructores para las clases bajo prueba y crea pruebas JUnit a partir de estas
- Simulant: una herramienta de Clojure que ejecuta simulaciones de varios agentes (por ejemplo, usuarios con diferentes perfiles de comportamiento) basándose en un modelo estadístico de su comportamiento, registrando todas las acciones y resultados en una base de datos para su posterior exploración y verificación.
- AutoTest - una herramienta integrada a EiffelStudio que prueba automáticamente el código Eiffel con contratos basados en el prototipo de investigación homónimo. [5] ·
- York Extensible Testing Infrastructure (YETI): una herramienta independiente del lenguaje que se dirige a varios lenguajes de programación (Java, JML, CoFoJa, .NET, C, Kermeta).
- GramTest es una herramienta de prueba aleatoria basada en gramática escrita en Java. Utiliza la notación BNF para especificar gramáticas de entrada.
Crítica
En la práctica, las pruebas aleatorias tienen un nicho especializado, principalmente porque rara vez se dispone de un oráculo efectivo, pero también debido a las dificultades con el perfil operativo y con la generación de valores de entrada pseudoaleatorios. [1]
Un oráculo de pruebas es un instrumento para verificar si los resultados coinciden con la especificación del programa o no. Un perfil de operación es un conocimiento sobre los patrones de uso del programa y, por lo tanto, qué partes son más importantes.
En el caso de los lenguajes de programación y plataformas que tienen contratos (por ejemplo, Eiffel, .NET o varias extensiones de Java como JML, CoFoJa...), los contratos actúan como oráculos naturales y el enfoque se ha aplicado con éxito. [5] En particular, las pruebas aleatorias encuentran más errores que las inspecciones manuales o los informes de los usuarios (aunque sean diferentes). [9]
Véase también
Referencias
- ^ abc Richard Hamlet (1994). "Pruebas aleatorias". En John J. Marciniak (ed.). Enciclopedia de ingeniería de software (1.ª ed.). John Wiley and Sons. ISBN 978-0471540021.
- ^ Agrawal, P.; Agrawal, VD (1 de julio de 1975). "Análisis probabilístico del método de generación de pruebas aleatorias para redes lógicas combinacionales irredundantes". IEEE Transactions on Computers . C-24 (7): 691–695. doi :10.1109/TC.1975.224289.
- ^ Duran, JW; Ntafos, SC (1 de julio de 1984). "Una evaluación de pruebas aleatorias". IEEE Transactions on Software Engineering . SE-10 (4): 438–444. doi :10.1109/TSE.1984.5010257.
- ^ ab Howden, William (1987). Análisis y pruebas funcionales de programas . Nueva York: McGraw Hill. pp. 51–53. ISBN 0-07-030550-1.
- ^ abc "AutoTest - Cátedra de Ingeniería de Software". se.inf.ethz.ch . Consultado el 15 de noviembre de 2017 .
- ^ ab "¿Es una mala práctica generar datos de prueba de forma aleatoria?". stackoverflow.com . Consultado el 15 de noviembre de 2017 .
- ^ Pacheco, Carlos; Shuvendu K. Lahiri; Michael D. Ernst; Thomas Ball (mayo de 2007). "Generación aleatoria de pruebas dirigida por retroalimentación" (PDF) . ICSE '07: Actas de la 29.ª Conferencia Internacional sobre Ingeniería de Software : 75–84. ISSN 0270-5257.
- ^ TY Chen; F.-C. Kuo; RG Merkel; TH Tse (2010), "Pruebas aleatorias adaptativas: el arte de la diversidad de casos de prueba", Journal of Systems and Software , 83 (1): 60–66, doi :10.1016/j.jss.2009.02.022, hdl : 10722/89054
- ^ Ilinca Ciupa; Alexander Pretschner; Manuel Oriol; Andreas Leitner; Bertrand Meyer (2009). "Sobre el número y la naturaleza de los fallos encontrados mediante pruebas aleatorias". Pruebas de software, verificación y fiabilidad . 21 : 3–28. doi :10.1002/stvr.415.
Enlaces externos
- Pruebas aleatorias de Andrea Arcuri.
- Pruebas aleatorias por Richard Hamlet, profesor emérito de la Universidad Estatal de Portland; una valiosa lista de recursos al final del artículo
- Wiki de pruebas aleatorias en Cunningham & Cunningham, Inc.