CAPÍTULO 6
LAS PRUEBAS SOFTWARE
Es un elemento crítico para garantizar la calidad del producto. El asegurar el buen funcionamiento de un producto software es un hecho que cada vez tiene más importancia.
La prueba es un paso destructivo consistente en intentar derribar el trabajo realizado con el fin de verificar y validar el software.
Una verificación se puede definir como:
El proceso de evaluación de un sistema o uno de sus componentes para determinar si los productos de una fase dada satisfacen las condiciones impuestas al comienzo de dicha fase.
Por otra parte la validación puede definirse como:
El proceso de evaluación de un sistema o uno de sus componentes durante o al final del proceso de desarrollo para determinar si satisface los requisitos específicados.
Es conveniente conocer algunos de los términos que se utilizan en esta fase del ciclo de vida:
§ Prueba:
Actividad en la que un sistema o algunos de sus componentes se ejecutan bajo circunstancias especificadas y que da unos resultados que se observan, registran y evalúan.
§ Caso de prueba:
Conjunto de entradas, condiciones de ejecución y resultados esperados desarrollados para un objetivo particular.
§ Defecto:
Defecto software como un proceso, definición de datos, procesamiento... incorrectos.
§ Fallo:
Incapacidad del sistema o algunos de sus componentes para realizar una función.
§ Error: Varias acepciones:
- La diferencia entre el valor calculado y el valor verdadero.
- Un defecto.
- Un resultado incorrecto.
- Acción humana que conduce a un resultado erróneo.
6.1. OBJETIVO DE LAS PRUEBAS
Las principales características del software (ausencia de leyes que rijan su comportamiento) hacen difícil la tarea de probarlo. Es más, estas características hacen imposible la realización una prueba exhaustiva al software.
Se ha de tomar una actitud o filosofía para realizar esta labor. Sería la siguiente:
§ El objetivo de una prueba es descubrir un error.
§ Un buen caso de prueba es aquel que tiene una gran probabilidad de descubrir un error no descubierto hasta entonces.
§ Una prueba tiene éxito si descubre un error no detectado hasta entonces.
Si con las pruebas no se detectan errores se llega a la conclusión de que el software parece funcionar de acuerdo a las especificaciones alcanzando los requisitos de rendimiento. Sin embargo esto no garantiza la ausencia de errores.
Myers realiza las siguientes recomendaciones para las pruebas:
1. Cada caso de prueba debe definir el resultado de salida esperado. Si discrepancia es síntoma de error y posible defecto en el software.
2. El programador debe evitar ser quien realice las pruebas puesto que éstas serían menos rigurosas de lo deseable.
3. Se debe hacer una inspección detallada del resultado de cada prueba para no pasar por alto síntomas de error.
4. Al realizar una prueba se debe hacer con datos de entrada válidos y no válidos o inesperados.
5. Una prueba debe comprobar por un lado que el software hace lo que debe hacer y por otro que el software no hace lo que no debe hacer.
6. Se deben evitar casos desechables, es decir, es necesario documentar o guardar los casos para así no repetir constantemente el diseño de casos de prueba.
7. No deben hacerse planes de prueba suponiendo que apenas hay errores, sino asumiendo que siempre los hay y hay que detectarlos.
8. Parece ser que donde se encuentra un defecto suele haber más.
9. Las pruebas son una tarea creativa, debiendo recurrir al ingenio y hacer uso de los recursos disponibles para alcanzar un buen nivel de detección de defectos.
6.2. EL PROCESO DE PRUEBA
El primer paso es generar un plan de pruebas base en base a la documentación sobre el proyecto y la documentación sobre el software a probar. A partir de ´este se generan pruebas específicificas. Una vez detalladas se coge la configuración del software a probar para ejecutar sobre ella los casos. Las salidas se deberán documentar y comprobar con la salida esperada. A partir de ésta se van a realizar dos actividades:
1. La depuración:
Para intentar corregir los defectos, aunque no siempre se asegura la corrección por lo que una vez realizada se deberá volver a probar (con las pruebas anteriores y/o con otras nuevas) el software para ver si está resuelto.
2. Análisis de la estadística de errores:
Para predecir errores y detectar causas habituales de error.
A medida que se van realizando pruebas se va viendo la calidad del software que se está analizando. Si se obtienen errores serios es debido la calidad del diseño y del software quedan en entredicho. Si se obtienen errores leves es o porque es un software de calidad y fiable o bien porque las pruebas no son adecuadas.
Si la prueba no descubre errores queda la sospecha de que la prueba no es demasiado buena, y lo normal es que el usuario vaya descubriendo los errores de forma eventual.
6.3. TÉCNICAS DE DISEÑO DE LOS CASOS DE PRUEBA
Como se dijo anteriormente realizar una prueba exhaustiva del software es imposible. Los casos de prueba se limitan, por tanto, a ver si el software tiene un nivel de confianza aceptable intentando detectar los defectos existentes sin necesidad de consumir una cantidad excesiva de recursos.
Para detectar los errores se ha de realizar una selección de pruebas pero no de forma aleatoria, sino aquellas que se consideren representativas de un conjunto de ellas. Para ello podemos atender a los siguientes criterios:
§ El enfoque estructural (caja blanca):
Se centra en la estructura interna y es una prueba exhaustiva que busca probar todos los posibles caminos de ejecución.
§ El enfoque funcional (caja negra):
Estudia la especificación de las funciones y las E/S. La prueba exhaustiva consiste en probar todas las posibles entradas y salidas del programa.
§ El enfoque aleatorio:
Utiliza modelos que representan las posibles entradas al programa a partir de los cuales crear casos de prueba. La prueba exhaustiva consiste en probar todas las posibles entradas al programa.
6.3.1. LAS PRUEBAS ESTRUCTURALES (CAJA BLANCA)
En un programa de 50 líneas con 25 sentencias IF en serie, existe un número total de caminos que contienen 33,5 millones de sentencias potenciales (contando dos posibles salidas para cada IF que suponen 225 posibles caminos).
Para diseñar los casos hay que elegir los caminos importantes que ofrezcan una seguridad aceptable en descubrir defectos. Para ello se utilizan criterios de cobertura lógica que:
§ Garanticen que se ejecutan al menos una vez todos los caminos independientes de cada módulo.
§ Se ejercitan todas las decisiones lógicas en sus caras verdaderas y falsas.
§ Se ejecutan todos los bucles en sus límites y con sus límites opcionales.
§ Se ejecutan las estructuras de datos internas para asegurar su validez.
Estas técnicas no requieren ninguna representación gráfica, aunque es habitual el uso de diagramas de flujo de control. Realización de estos diagramas:
1. Señalar sobre el código cada condición de cada decisión.
2. Agrupar el resto de sentencias situadas entre cada dos condiciones según los esquemas de representación de las estructuras básicas.
3. Numerar tanto condiciones como grupos de sentencias, asignándole un identificador único. Para ello se recomienda:
a) Alterar el orden de las restricciones en decisiones multicondicionales de mayor a menor restricción.
b) Identificar los nodos que representan condiciones asignándoles una letra y señalar cual es el resultado de las aristas que surgen de ellos. Por ejemplo, de una condición saldrán dos aristas, VERDADERO y FALSO, pues estos valores representarlos en cada una de ellas.
Myers propone la siguiente clasificación para los criterios de cobertura lógica:
§ Cobertura de sentencias:
Generar casos de prueba para que se ejecuten todas las sentencias del programa al menos una vez.
§ Cobertura de decisiones:
Escribir suficientes casos como para que se de en cada decisión al menos una vez un valor verdadero y falso. Engloba a la cobertura de sentencias.
§ Cobertura de condiciones:
Diseñar tantos casos de prueba como para que se den los dos resultados de las condiciones al menos una vez en todas las decisiones.
§ Cobertura de decisión/condición:
Hacer que se cumpla tanto el criterio de condiciones haciendo que se cumpla el criterio de decisiones.
§ Criterio de condición múltiple:
Consiste en descomponer una decisión multicondicional en varias decisiones unicondicionales para así evaluar todas las combinaciones posibles de soluciones en una condición.
PRUEBAS DEL CAMINO BÁSICO
La secuencia de sentencias encadenadas desde la sentencia del final hasta su sentencia final.
Debido a que existe una gran cantidad de caminos posibles se intenta hacer una reducción. Para ello se introduce el concepto de camino de prueba:
Camino del programa que atraviesa, como máximo una vez, el interior de cada bucle que encuentra.
McCabe propuso la prueba del camino básico como un tipo de prueba de caja blanca. Este camino permite al diseñador derivar una medida de la complejidad lógica y usarla como guía de un conjunto de caminos básicos de ejecución. Esta medida es complejidad ciclomática y aporta el límite superior para el número de pruebas que se deben realizar para asegurar que cada sentencia se ejecuta al menos una vez.
Un camino independiente es cualquier camino del programa que introduce por lo menos un nuevo conjunto de sentencias de procesamiento o una nueva condición. En términos del grafo de flujo, un camino independiente se debe mover por lo menos por una arista que no haya sido recorrida anteriormente.
La complejidad ciclomática se puede expresar mediante una de las siguientes expresiones:
V(G)= A- N+2 Donde: A: Número de aristas.
V(G)= R N: Número de nodos.
V(G)= C+1 R: Número de regiones cerradas del grafo.
C: Número de nodos predicado (condición) del grafo.
Un nodo predicado es aquel que contiene una condición y se caracteriza porque de ´el emergen dos o más aristas.
Procedimiento a seguir para la derivación de los casos de prueba:
1. Dibujar el grafo de flujo.
2. Determinar la complejidad ciclomática.
3. Determinar el conjunto básico de caminos linealmente independientes.
4. Preparar los casos de prueba que forzarán la ejecución de cada camino del conjunto básico.
Es posible mecanizar la determinación del conjunto básico de caminos a través de la matriz de conexiones. El cálculo de de la complejidad ciclomática se puede calcular también haciendo uso de esta matriz, añadiéndole una nueva columna en el que cada elemento será la suma de los elementos fila de dicho elemento menos uno. La complejidad ciclomática será el sumatorio de los elementos de esta columna más uno.
La experimentación con métrica de McCabe ha dado las siguientes conclusiones:
1. V (G) marca un límite mínimo del número de casos de prueba para un programa (cada condición como un nodo individual).
2. Si V (G) es mayor de 10 y no es debida a sentencias CASE-OF o similares, la probabilidad de error crece mucho. Conviene en estos casos modularizar más.
LA PRUEBA DE LOS BUCLES
Técnica de prueba que se centra en validar las construcciones de los bucles. Se distinguen cuatro tipos de bucles:
§ Bucles simples: Pruebas a realizar (n: máximo de pasos en el bucle):
1. Pasar por alto el bucle.
2. Pasar una sola vez por el bucle.
3. Pasar dos veces por el bucle.
4. Pasar m veces por el bucle siendo m <>
5. Pasar n − 1, n y n + 1 veces por el bucle.
§ Bucles anidados:
Para evitar la realización de un número muy elevado de pruebas debemos usar la siguiente técnica propuesta por Beizer.
1. Comenzar en el bucle más exterior. Establecer los demás bucles en sus valores mínimos.
2. Realizar las pruebas de los bucles simples para el bucle más interior manteniendo los contadores de los bucles exteriores en sus valores mínimos. Añadir otras pruebas para valores fuera de rango.
3. Progresar hacia fuera llevando a cabo pruebas para el siguiente bucle (en orden de anidamiento) pero manteniendo todos los bucles externos en sus valores mínimos y los demás bucles anidados en sus valores típicos.
4. Continuar hasta que se hayan probado todos los bucles.
§ Bucles concatenados:
Si los bucles son independientes conviene usar el procedimiento para bucles simples, si no se recomienda usar el de los bucles anidados.
§ Bucles no estructurados:
Deben rediseñarse siempre que sean posible a las construcciones de programación estructurada.
6.3.2. LA PRUEBA FUNCIONAL (CAJA NEGRA)
Se centra en el estudio de la especificación del software, análisis de las funciones que debe realizar, de las entradas y salidas. Como ocurre con la prueba estructural, una prueba exhaustiva de la caja negra es impracticable por lo que es necesario elegir una serie de criterios para realizar una serie de pruebas que aporten un nivel lo más alto de fiabilidad posible.
El que cubre un conjunto extenso de otros posibles casos, es decir, nos indica algo acerca de la ausencia o la presencia de defectos en el conjunto específico de entradas que prueba, así como de otros conjuntos similares.
Este tipo de pruebas se centra en descubrir errores como:
§ Funciones incorrectas o ausentes.
§ Errores de la interfaz.
§ Errores en estructuras de datos o acceso a bases de datos externas.
§ Errores de rendimiento.
§ Errores de inicialización y terminación.
Se puede considerar por tanto un método complementario a las pruebas estructurales.
PARTICIÓN O CLASES DE EQUIVALENCIA
§ Cada caso debe cubrir el máximo número de entradas.
Para identificar las clases de equivalencia se pueden seguir los siguientes pasos:
1. Identificación de las condiciones de las entradas del programa.
2. Identificación de las clases de equivalencia para datos válidos y no válidos usando el principio de equivalencia enunciado a continuación:
Existen algunas reglas que ayudan a identificar las clases de equivalencia:
§ Regla 1:
Para un rango de valores [a, b]. Se crean dos clases no válidas (valor <>) y (valor > b), y una válida (a ≤ valor ≤ b).
§ Regla 2:
Para un número de valores: de a a b. Se crean dos clases no válidas (valor <>), (valor > b), y una válida (a ≤ valor ≤ b).
§ Regla 3:
Para situaciones del tipo debe ser o booleana, se crean dos clases, una válida y otra no válida.
§ Regla 4:
Para un conjunto de valores admitido tantas clases válidas como valores admitidos y una clase no válida.
§ Regla 5:
Si se sospecha que en una clase hay elementos que se tratan de forma distinta que al resto de la clase debe dividirse la clase.
Estas reglas nos permiten desarrollar casos de prueba para cada elemento del dominio de entrada. El proceso de identificación de estos casos de prueba consiste en:
1. Asignación de un número único a cada clase de equivalencia.
2. Escribir por cada clase de equivalencia válida un caso de prueba.
3. Escribir por cada clase de equivalencia no válida un caso de prueba.
Separamos clases válidas y no válidas para que se detecten todos los defectos a la hora de evaluar un dato.
ANÁLISIS DE VALORES LÍMITE (AVL)
La experiencia ha dejado en constancia que los casos de prueba que exploran las condiciones límite de un programa producen un mejor resultado para la detección de defectos.
Esta técnica complementa a la técnica de clases de equivalencia y se caracteriza por:
§ Requiere la selección de no sólo uno, sino de al menos un elemento para que los márgenes se sometan a prueba.
§ Se centran no sólo en el dominio de entrada sino también en el de salida.
El proceso de selección es heurístico, pero podría ajustarse a las siguientes reglas:
§ Regla 1 (entrada):
Para un rango [a, b], se crearán dos casos con los límites, (a) y (b) y otros dos con valores fuera del rango.
§ Regla 2 (entrada):
Para un número de valores comprendido entre a y b, se crearán cuatro casos: (a − 1, a, b, b + 1).
§ Regla 3 (salida):
Para salidas que deben estar dentro de un rango. Usar la regla 1 para intentar obtener una serie de valores de salida dentro de este rango límite y fuera de estos límites.
§ Regla 4 (salida):
Para salidas que deben tener un valor comprendido entre dos valores.
Usar la regla 2 para intentar obtener una serie de valores de salida dentro límite de valores admitidos y fuera de este límite (en una unidad).
En las reglas 3 y 4 hay que considerar que no siempre se va a poder obtener valores fuera de los límites y que los valores de entrada límite no generan valores de salida límite.
CONJETURA DE ERRORES
Técnica que consiste en intentar detectar errores haciendo uso de la experiencia y viendo las situaciones propensas a error que suelen provocar fallos para así realizar una extrapolación al programa analizado e intentar descubrirle errores. Por ello a esta técnica se le suele denominar generación de casos (o valores) especiales.
Algunos ejemplos serían considerar el cero es un valor propenso a error, o, para el tratamiento de listas, tener en cuenta que los casos en los que no hay valores o hay un solo valor suelen ser conflictivos...
6.3.3. PRUEBAS ALEATORIAS
Consiste en la prueba de programas haciendo uso de entradas creadas de forma aleatoria por un generador de pruebas alimentado con una descripción de entradas y las secuencias de entrada posibles y su probabilidad de ocurrir en la práctica.
Si el proceso de generación ha sido correcto se crearán eventualmente todas las entradas posibles del programa o incluso si se hace uso de una distribución de estadística que indique la frecuencia en que se producen las entradas se podrían realizar pruebas que se ajustan más a la realidad.
Esta técnica es usada sobre todo en compiladores. En el diseño de software se usa sobre todo las técnicas de la caja blanca y la caja negra.
6.4. EJECUCIÓN DE LAS PRUEBAS
6.4.1. EL PROCESO DE EJECUCIÓN
Atendiendo al estándar IEEE 1008 la ejecución de las pruebas abarca las siguientes fases:
1. Ejecutar las pruebas.
2. Ver si ha concluido el proceso de prueba.
3. Si han concluido, evaluar resultados. Si no hay que generar casos adicionales que satisfagan los criterios de compleción de pruebas.
El proceso de ejecución consta de:
1. Se ejecutan las pruebas.
2. Se comprueba si ha habido algún fallo de ejecución.
3. Si se ha producido un fallo puede ser debido o a un defecto software o a un error en el diseño de pruebas. Se deberá corregir el error y volver al paso 1.
El proceso de comprobación de terminación de las pruebas consta de:
1. Se comprueba si se cumplen los criterios de compleción de las pruebas.
2. Si las pruebas han concluido se evalúan los productos en base a los resultados obtenidos (terminación normal).
3. Si no han acabado se debe comprobar que no hay condiciones anormales en la prueba.
4. Si hay condiciones anormales se pasa a la evaluación (terminación anormal), si no las hay se deberá generar y ejecutar pruebas adicionales.
6.5. LA DEPURACIÓN
Tiene dos etapas:
1. Localización de defecto.
2. Corrección del defecto, efectuando las modificaciones necesarias en el software.
A partir de los síntomas de defectos de error resultado del proceso de prueba, se pasa a un proceso de detección y corrección del error en el proceso de depuración. Si no se detecta dicho error se pasará a la generación de más pruebas que ayuden a detectarlo. Una vez corregido el error se efectuarán nuevas pruebas que comprueben si se ha eliminado el problema.
6.6. ESTRATEGIA DE APLICACIÓN DE LAS PRUEBAS
Técnica que pretende dividir la aplicación y planificación de las pruebas haciendo uso de niveles de prueba. Cada nivel se va a centrar en probar el software en referencia al trabajo realizado en una etapa de desarrollo diferente, haciendo distinguir cinco niveles:
1. Prueba módulo.
2. Prueba de integración.
3. Prueba de validación.
4. Prueba del sistema.
5. Prueba de aceptación.
6.6.1. PRUEBA UNIDAD
Centra sus actividades en ejercitar la lógica de módulo (caja blanca) y los distintos aspectos de la especificación de las funciones (caja negra).
Esta prueba abarca desde un módulo, hasta un grupo de módulos o incluso un programa entero y suele ser llevada a cabo por el personal de desarrollo.
6.6.2. LA PRUEBA DE INTEGRACIÓN
Está totalmente ligada a la forma en que los distintos componentes software están integrados. Comprende por tanto una serie de pruebas realizadas de forma progresiva desde módulos hasta el sistema completo. Su objetivo es comprobar los flujos de datos entre los distintos componentes del sistema.
Existen dos tipos fundamentales de integración:
§ Integración incremental:
Un módulo se prueba con el resto de módulos ya probados. Si funciona pasa al conjunto de los probados. En función del orden elegido esta integración puede ser ascendente o descendente, que, a su vez, puede ser en anchura o en profundidad.
§ Integración no incremental (Big-Bang):
Se prueba cada módulo de forma independiente y después se integran todos para probar el programa completo.
INTEGRACIÓN INCREMENTAL ASCENDENTE
1. Se combinan módulos de bajo nivel en grupos que realizan alguna subfunción específica.
2. Se escribe para cada módulo cuál es su impulsor o conductor que va a permitir simular la llamada a los módulos del grupo.
3. Se prueba cada grupo empleando su impulsor.
4. Se eliminan los módulos impulsores de cada grupo y se sustituyen por los módulos de nivel superior. Se vuelven a construir impulsores para éstos y se vuelven a probar, así hasta llegar a la raíz de la jerarquía.
INTEGRACIÓN INCREMENTAL DESCENDENTE
Comienza por el módulo raíz de la jerarquía de módulos y va incorporando nuevos módulos de forma progresiva. En este caso no va a haber un procedimiento que indique qué modulo es mejor incorporar antes.
Es similar a la anterior sólo que en vez de usar módulos impulsores usan módulos stubs que primero se supondrá que funcionan correctamente para probar el módulo de nivel superior y luego se sustituirán por los originales y se descenderá en la jerarquía.
La construcción de módulos stubs es más compleja. Se distinguen cuatro grados distintos de complejidad:
§ Tipo 1: Muestra un mensaje de traza.
§ Tipo 2: Muestra un mensaje que va a depender de los parámetros que se le pase.
§ Tipo 3: Devuelve un valor que no depende de los parámetros que recibe.
§ Tipo 4: Devuelve un valor que depende, en mayor o menor grado, de los parámetros que recibe.
INTEGRACIÓN NO INCREMENTAL
En esta integración cada módulo requiere:
1. Un módulo impulsor.
2. Uno o más módulos ficticios.
Cada módulo es probado de forma independiente para luego realizar una prueba del sistema a nivel global.
6.6.3. LA PRUEBA DE VALIDACIÓN
Debe comprobar si existen desajustes entre el software y los requisitos fijados para su funcionamiento en la especificación de los mismos.
6.6.4. LA PRUEBA DEL SISTEMA
El objetivo de esta prueba es ver si se cumplen todos los requisitos especificados (funcionales, de rendimiento, en documentación correcta y ejecución y buen rendimiento en condiciones límite)
Los casos de prueba se diseñan generalmente teniendo en cuenta tres fuentes:
1. Técnicas de caja negra.
2. Stress testing, pruebas de rendimiento del sistema y su capacidad funcional.
3. Técnicas de la caja blanca.
6.6.5. LA PRUEBA DE ACEPTACIÓN
Prueba cuyo objetivo es comprobar que el producto está listo para ser implantado dando fe al usuario del que el producto es fiable, robusto, fácil de usar y, sobre todo, que cumple los requisitos marcados.
No hay comentarios:
Publicar un comentario