Tema 4. Diseño y realización de pruebas Página 1 de 22 Tema 4. Diseño y realización de pruebas Contenido 1 Introducción .......................................................................................................................... 2 2 Planificación de las pruebas .................................................................................................. 3 3 Técnicas de diseño de casos de prueba ................................................................................ 4 4 3.1 Pruebas funcionales o de caja negra ............................................................................. 5 3.2 Pruebas estructurales o de caja blanca ......................................................................... 6 3.3 Pruebas de regresión .................................................................................................... 7 Estrategias de prueba del software ...................................................................................... 8 4.1 PRUEBA DE UNIDAD ...................................................................................................... 8 4.2 PRUEBA DE INTEGRACIÓN............................................................................................. 9 4.3 PRUEBA DE VALIDACIÓN ............................................................................................... 9 4.4 PRUEBA DEL SISTEMA ................................................................................................... 9 5 Documentación para las pruebas........................................................................................ 10 6 Pruebas de código ............................................................................................................... 10 6.1 PRUEBA DE CUBRIMIENTO O DEL CAMINO BÁSICO ................................................... 10 6.2 PARTICIÓN O CLASES DE EQUIVALENCIA .................................................................... 13 6.3 ANÁLISIS DE VALORES LÍMITE ..................................................................................... 14 7 Herramientas de depuración .............................................................................................. 14 8 Pruebas unitarias JUnit........................................................................................................ 15 Tema 4. Diseño y realización de pruebas Página 2 de 22 1 Introducción Las pruebas de software se realizan para verificar y validar un producto software antes de su puesta en marcha. Consiste en probar la aplicación construida. Se integran dentro del ciclo de vida del software. Durante todo el proceso de desarrollo de software (también llamado ciclo de vida del desarrollo de software), desde la fase de diseño, en la implementación y una vez desarrollada la aplicación, es necesario realizar un conjunto de pruebas que permitan verificar que el software que se está creando es correcto y cumple con las especificaciones requeridas por el usuario. En el proceso de desarrollo de software es muy fácil que se produzca un error humano. Estos errores humanos pueden ser: una incorrecta especificación de los objetivos, errores producidos durante el proceso de diseño y errores que aparecen en la fase de desarrollo. Un error, que desde el punto de vista de codificación puede ser relativamente simple de corregir, puede resultar muy difícil y costoso de detectar y puede llegar a tener graves efectos en la organización. Mediante la realización de pruebas de software, se van a realizar las tareas de verificación (comprobar que el software cumple los requisitos especificados) y validación (comprobar si el software hace lo que el usuario deseaba. Tiene que estar verificado). La verificación es la comprobación de que un sistema o uno de sus componentes satisfacen las condiciones impuestas al comienzo de dicha fase. Con la verificación comprobamos si estamos construyendo correctamente la aplicación. La validación es el proceso de evaluación del sistema o de uno de sus componentes, para determinar si satisface los requisitos especificados. Comprobamos si estamos construyendo la aplicación correcta. Para llevar a cabo el proceso de pruebas es necesario hacer una planificación de las mismas y contar con un grupo especializado en procesos de evaluación de calidad y específicamente en pruebas de software, que por medio de un método establecido y con herramientas adecuadas para la realización de las mismas permita verificar y validar el software. Antes de continuar es necesario conocer las definiciones de los siguientes términos: Prueba (test): “Una actividad en la cual un sistema o uno de sus componentes se ejecuta en circunstancias previamente especificadas, los resultados se observan y registran y se realiza una evaluación de algún aspecto”. Caso de prueba (test case): “Un conjunto de entradas, condiciones de ejecución, y resultados esperados desarrollados para un objetivo particular como, por ejemplo, ejercitar un camino concreto de un programa o verificar el cumplimiento de un determinado requisito, incluyendo toda la documentación asociada”. Defecto (defect, fault, “bug”): “Un defecto en el software como, por ejemplo, un proceso, una definición de datos o un paso de procesamiento incorrectos en un programa”. Fallo (Failure): “La incapacidad de un sistema o de alguno de sus componentes para realizar las funciones requeridas dentro de los requisitos de rendimientos especificados”. Tema 4. Diseño y realización de pruebas Página 3 de 22 Error (error): o “La diferencia entre un valor calculado, observado o medido y el valor verdadero, especificado o teóricamente correcto”. o Una acción humana que conduce a un resultado incorrecto (una metedura de pata). Por ejemplo, que el operador o el programador pulse una tecla equivocada. Depuración: El proceso de localizar, analizar y corregir los defectos que se sospecha que contiene el software. Verificación: o El proceso de evaluación de un sistema o de uno de sus componentes para determinar si los resultados de una fase dada satisfacen las condiciones impuestas al comienzo de dicha fase. o Un conjunto de actividades que aseguran que el software implementa correctamente una función específica. Validación: o El proceso de evaluación de un sistema o de uno de sus componentes durante o al final del proceso de desarrollo para determinar si el software hace lo que el usuario deseaba. o Un conjunto de actividades que aseguran que el software construido se ajusta a los requisitos del cliente. La ejecución de pruebas involucra una serie de etapas: Planificación de las pruebas Diseño y construcción de los casos de prueba Definición de los procedimientos de prueba Ejecución de las pruebas Registro de resultados obtenidos Registro de errores encontrados Depuración de los errores Informe de los resultados obtenidos 2 Planificación de las pruebas Para llevar a cabo el proceso de pruebas, de manera eficiente, es necesario implementar una estrategia de pruebas. Siguiendo el Modelo en Espiral, las pruebas empezarían con la prueba de unidad, donde se analizaría el código implementado y seguiríamos en la prueba de integración, donde se prestan atención al diseño y la construcción de la arquitectura del software. El siguiente paso sería la prueba de validación, donde se comprueba que el sistema construido cumple con lo establecido en el análisis de requisitos de software. Finalmente se Tema 4. Diseño y realización de pruebas Página 4 de 22 alcanza la prueba de sistema que verifica el funcionamiento total del software y otros elementos del sistema. Pruebas de unidad: se centran en la unidad más pequeña de software, el módulo. Pruebas de integración: se construye con los módulos una estructura de programa tal como dicta el diseño. Prueba de validación o aceptación: prueba del software en el entorno real de trabajo por el usuario final. Se validan los requisitos establecidos. Prueba del sistema: verifica que cada elemento encaja de forma adecuada y se alcanza la funcionalidad y rendimiento total. 3 Técnicas de diseño de casos de prueba El diseño de casos de prueba está totalmente condicionado por la imposibilidad de probar exhaustivamente el software, ya que no se pueden probar todos los casos posibles en el desarrollo del mismo. Las técnicas de diseño de casos de prueba tienen como objetivo conseguir una confianza aceptable en que se detectarán los defectos existentes sin que se necesite consumir una cantidad excesiva de recursos. Hay que llegar a un equilibrio entre la disponibilidad de recursos y la confianza que aportan los casos para descubrir los defectos existentes. La idea fundamental para el diseño de casos de prueba consiste en elegir algunas pruebas que, por sus características, se consideren representativas del resto. La dificultad de esta idea es saber elegir los casos que se deben ejecutar. De hecho, una elección puramente aleatoria no proporciona demasiada confianza en que se puedan detectar todos los defectos existentes. Un caso de prueba es un conjunto de entradas, condiciones de ejecución y resultados esperados, desarrollado para conseguir un objetivo particular o condición de prueba. Para llevar a cabo un caso de prueba es necesario definir las precondiciones y post condiciones, identificar unos valores de entrada y conocer el comportamiento que debería tener el sistema ante dichos valores. Tras realizar ese análisis e introducir los datos, se observa si el comportamiento es el previsto o no y por qué. Determinamos con esto si el sistema ha pasado la prueba o no. En la ingeniería del software, nos encontramos con dos enfoques fundamentales para diseñar casos de prueba: Tema 4. Diseño y realización de pruebas Página 5 de 22 3.1 Pruebas funcionales o de caja negra Se llevan a cabo sobre la interfaz del software. No hace falta conocer la estructura interna del programa ni su funcionamiento. Pretende obtener casos de prueba que demuestren que las salidas que devuelve la aplicación son las esperadas y que la integridad de la información externa se mantiene. La prueba ideal consistiría en probar todas las posibles entradas y salidas del programa. El sistema se considera como una caja negra cuyo comportamiento solo se puede determinar estudiando las entradas y las salidas que devuelve en función de las entradas suministradas. Con este tipo de pruebas se pretende encontrar errores como por ejemplo: Funcionalidades incorrectas o ausentes. Errores de interfaz. Errores en estructuras de datos o en accesos a bases de datos externas. Errores de rendimiento. Errores de inicialización y finalización. Dentro de las pruebas funcionales, podemos indicar cuatro tipos de pruebas: Particiones equivalentes: La idea de este tipo de pruebas funcionales, es considerar el menor número posible de casos de pruebas, para ello, cada caso de prueba tiene que abarcar el mayor número posible de entradas diferentes. Lo que se pretende, es crear un conjunto de clases de equivalencia, donde la prueba de un valor representativo de la misma, en cuanto a la verificación de errores, sería extrapolable al que se conseguiría probando cualquier valor de la clase. Análisis de valores límite: En este caso, a la hora de implementar un caso de prueba, se van a elegir como valores de entrada, aquellos que se encuentran en el límite de las clases de equivalencia. Pruebas aleatorias: Consiste en generar entradas aleatorias para la aplicación que hay que probar. Se suelen utilizar generadores de prueba, que son capaces de crear un volumen de casos de prueba al azar, con los que será alimentada la aplicación. Este tipo de pruebas, se suelen utilizar en aplicaciones que no sean interactivas, ya que es muy difícil generar las secuencias de entrada adecuadas de prueba, para entornos interactivos. Hipótesis de errores: se basan en la experiencia de dónde aparecen los errores más habituales en las fases de análisis y desarrollo. Los errores se buscan dónde se prevé que suelen presentarse. Es un método fuertemente dependiente de la experiencia. Tema 4. Diseño y realización de pruebas Página 6 de 22 3.2 Pruebas estructurales o de caja blanca Se basan en examinar minuciosamente los detalles procedimentales del código de la aplicación. Se pretende verificar la estructura interna de cada componente de la aplicación, independientemente de la funcionalidad establecida para el mismo. Este tipo de pruebas, no pretende comprobar la corrección de los resultados producidos por los distintos componentes, su función es comprobar que se van a ejecutar todas las instrucciones del programa, que no hay código no usado, comprobar que los caminos lógicos del programa se van a recorrer, se utilizan las decisiones en su parte verdadera y en su parte falsa, etc. Mediante esta técnica se pretende obtener casos que garanticen: Cobertura de sentencias: se han de generar casos de pruebas suficientes para que cada instrucción del programa sea ejecutada, al menos, una vez. Cobertura de decisiones: se trata de crear los suficientes casos de prueba para que cada opción resultado de una prueba lógica del programa, se evalúe al menos una vez a cierto y otra a falso. Cobertura de condiciones: se trata de crear los suficientes casos de prueba para que cada elemento de una condición, se evalúe al menos una vez a falso y otra a verdadero. Cobertura de condiciones y decisiones: consiste en cumplir simultáneamente las dos anteriores. Cobertura de caminos: es el criterio más importante. Establece que se debe ejecutar al menos una vez cada secuencia de sentencias encadenadas, desde la sentencia inicial del programa, hasta su sentencia final. La ejecución de este conjunto de sentencias, se conoce como camino. Como el número de caminos que puede tener una aplicación, puede ser muy grande, para realizar esta prueba, se reduce el número a lo que se conoce como camino básico. La correcta ejecución de bucles, que pueden ser: simples, anidados, concatenados y no estructurados. ○ A los bucles simples se les debe aplicar el siguiente conjunto de pruebas, donde n es el número máximo de pasos permitidos por el bucle: Tema 4. Diseño y realización de pruebas Página 7 de 22 1. Saltarse el bucle. 2. Ejecutarlo sólo una vez. 3. Pasar dos veces. 4. Hacer m pasadas siendo m<n. 5. hacer n-1, n y n+1 pasos por el bucle. ○ ○ Los bucles concatenados se pueden probar mediante el enfoque anteriormente definido para los bucles simples, mientras cada uno de los bucles sea independiente del resto. Sin embargo, si hay dos bucles concatenados y se usa el controlador del bucle 1 como valor inicial del bucle 2, entonces los bucles no son independientes. Cuando los bucles no son independientes, se recomienda usar el enfoque aplicado para los bucles anidados. Bucles anidados. Si extendiéramos el enfoque de prueba de los bucles simples a los bucles anidados, el número de posibles pruebas aumentaría geométricamente a medida que aumenta el nivel de anidamiento. Esto llevaría a un número impracticable de pruebas. Para reducir el número de pruebas podemos: 1. Comenzar por el bucle más interior. Establecer o configurar los demás bucles con sus valores mínimos. 2. Llevar a cabo las pruebas de bucles simples para el bucle más interior, mientras se mantienen los parámetros de iteración (por ejemplo, contador del bucle) de los bucles externos en sus valores mínimos. Añadir otras pruebas para valores fuera de rango o excluidos. 3. Progresar hacia fuera, llevando a cabo pruebas para el siguiente bucle, 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 no estructurados. Siempre que sea posible, esta clase de bucles se deben rediseñar para que se ajusten a las construcciones de programación estructurada. Que se utilizan todas las estructuras de datos internas para asegurar su validez. 3.3 Pruebas de regresión Si durante el proceso de prueba detectamos un posible fallo o error tendremos que modificar el componente donde se ha detectado. Esta modificación, puede generar errores colaterales, que no existían antes. Como consecuencia, la modificación realizada nos obliga a repetir pruebas que hemos realizado con anterioridad. El objetivo de las pruebas de regresión, es comprobar que los cambios sobre un componente de una aplicación, no introduce un comportamiento no deseado o errores adicionales en otros componentes no modificados. Por ello, las pruebas de regresión se deben llevar a cabo cada vez que se hace un cambio en el sistema, tanto para corregir un error, como para realizar una mejora. No es suficiente Tema 4. Diseño y realización de pruebas Página 8 de 22 probar sólo los componentes modificados o añadidos, o las funciones que en ellos se realizan, sino que también es necesario controlar que las modificaciones no produzcan efectos negativos sobre el mismo u otros componentes. Normalmente, este tipo de pruebas implica la repetición de las pruebas que ya se hayan realizado previamente, con el fin de asegurar que no se introducen errores que puedan comprometer el funcionamiento de otros componentes que no han sido modificados y confirmar que el sistema funciona correctamente una vez realizados los cambios. En un contexto más amplio, las pruebas de software con éxito, son aquellas que dan como resultado el descubrimiento de errores. Como consecuencia del descubrimiento de errores, se procede a su corrección, lo que implica la modificación de algún componente del software que se está desarrollando, tanto del programa, de la documentación y de los datos que lo soportan. La prueba de regresión es la que nos ayuda a asegurar que estos cambios no introducen un comportamiento no deseado o errores adicionales. La prueba de regresión se puede hacer manualmente, volviendo a realizar un subconjunto de todos los casos de prueba o utilizando herramientas automáticas. El conjunto de pruebas de regresión contiene tres clases diferentes de clases de prueba: - Una muestra representativa de pruebas que ejercite todas las funciones del software. - Pruebas adicionales que se centran en las funciones del software que se van a ver probablemente afectadas por el cambio; - Pruebas que se centran en los componentes del software que han cambiado. Para evitar que el número de pruebas de regresión crezca demasiado, se deben de diseñar para incluir sólo aquellas pruebas que traten una o más clases de errores en cada una de las funciones principales del programa. No es práctico ni eficiente volver a ejecutar cada prueba de cada función del programa después de un cambio. 4 Estrategias de prueba del software Una vez conocidas las técnicas de diseño y ejecución de casos de prueba, se debe analizar cómo se plantea la utilización de las pruebas en el ciclo de vida del software. La estrategia de aplicación y la planificación de las pruebas pretenden integrar el diseño de los casos de prueba en una serie de pasos bien coordinados a través de la creación de distintos niveles de prueba, con diferentes objetivos. La estrategia de prueba va de lo particular a lo general: Pruebas de unidad: se comienza por las pruebas por separado de cada módulo del software. Pruebas de integración: a partir del esquema de diseño, los módulos probados se vuelven a probar combinados para probar sus interfaces. Prueba de validación o aceptación es la prueba para determinar si se cumplen los requisitos de aceptación marcados por el cliente. Prueba del sistema: se procede a realizar la prueba de un sistema integrado de hardware y software para comprobar si cumple los requisitos funcionales. 4.1 PRUEBA DE UNIDAD Se trata de probar cada módulo para eliminar errores en la interfaz y en la lógica interna. Utiliza técnicas de caja blanca y caja negra. Tema 4. Diseño y realización de pruebas Página 9 de 22 Las realiza el programador y están asociadas a los requisitos de la fase de Programación Se realizan pruebas sobre: La interfaz del módulo, para asegurar el flujo correcto. Las estructuras de datos locales, para asegurar que mantienen la integridad. Las condiciones límite. Todos los caminos independientes, para asegurar que todas sentencias se ejecutan al menos una vez. Todos los manejos de errores. 4.2 PRUEBA DE INTEGRACIÓN Se observa cómo interaccionan los distintos módulos cuando están combinados. Se hacen para validar los requisitos de la fase de Diseño. Dos enfoques para estas pruebas: Integración no incremental o big bang: Se prueba cada módulo por separado y luego se integran todos de una vez y se prueba el programa completo. Generalmente se encuentran muchos errores y la corrección es difícil. Integración incremental: El programa completo se va construyendo y probando en pequeños segmentos, así es más fácil localizar los errores. Puede ser de dos tipos: o Ascendente: la construcción y prueba empieza desde los niveles más bajos de la estructura del programa. o Descendente: la integración comienza en el módulo principal 4.3 PRUEBA DE VALIDACIÓN La validación se consigue cuando el software funciona de acuerdo con las expectativas y requisitos. Las realizan los usuarios junto con el jefe de proyecto y validan los requisitos de la fase de Análisis Se llevan a cabo una serie de pruebas de caja negra: Prueba Alfa: por el usuario o cliente en el lugar de desarrollo. El cliente usa el software y el desarrollador mira, apuntando los errores y problemas de uso. Pruebas Beta: por los usuarios finales en su lugar de trabajo. Sin el desarrollador delante. El usuario registra los problemas que encuentra (reales o imaginarios) e informa al desarrollador para que lo modifique y arregle. 4.4 PRUEBA DEL SISTEMA Es un conjunto de pruebas que se hacen cuando el software está funcionando como un todo, para verificar el programa final después de que todos los componentes de software y hardware han sido integrados: Prueba de recuperación: se fuerza el fallo y se verifica que la recuperación se lleva a cabo apropiadamente. Prueba de seguridad: intenta verificar que el sistema está protegido contra accesos ilegales. Tema 4. Diseño y realización de pruebas Página 10 de 22 Prueba de resistencia (stress): trata de enfrentar al sistema con situaciones que exigen gran cantidad de recursos (máximo de memoria, gran frecuencia de datos de entrada, problemas en un sistema operativo virtual, conectividad, simulación de ataques…) 5 Documentación para las pruebas Existen una serie de documentos que se han de producir durante el proceso de pruebas: Plan de pruebas: describe el alcance, el enfoque, los recursos y el calendario de las actividades de prueba. Identifica los elementos a probar, las características a probar, las tareas que se van a realizar, quién las va a hacer, y los riesgos asociados al plan. Informes de pruebas. Cuatro documentos: o un informe que identifica los elementos que están siendo probados, o un registro de las pruebas (donde se registra lo que ocurre durante la ejecución de la prueba), o un informe de incidencias de prueba (cualquier evento que se produzca durante la ejecución de la prueba que requiera mayor investigación) o un informe resumen de las actividades de prueba. Especificaciones de prueba, formadas por tres documentos: o La especificación del diseño de la prueba: requisitos, casos de prueba y procedimientos necesarios para llevar a cabo las pruebas, y los criterios de pasa – no pasa. o La especificación de los casos de prueba: documenta los valores reales utilizados para la entrada, junto con los resultados previstos. o La especificación de los procedimientos de prueba: se identifican los pasos necesarios para hacer funcionar el sistema y ejecutar los casos de prueba especificados. 6 Pruebas de código Consiste en ejecutar el programa o parte de él con el objetivo de encontrar errores. Se parte de un conjunto de entradas y una serie de condiciones de ejecución. Se observan y registran los resultados y se comparan con los resultados esperados. Se observará si el comportamiento del programa es el previsto o no y por qué. A continuación vemos algunas técnicas para las pruebas de código: 6.1 PRUEBA DE CUBRIMIENTO O DEL CAMINO BÁSICO Es una técnica de prueba de caja blanca que permite al diseñador de las pruebas: Obtener una medida de la complejidad lógica de un diseño procedimental. Usar esa medida como guía para la definición de un conjunto básico de caminos de ejecución. Los casos de prueba obtenidos del conjunto básico garantizan que durante la prueba se ejecuta por lo menos una vez cada sentencia del programa. Tema 4. Diseño y realización de pruebas Página 11 de 22 Para la obtención de la medida de complejidad lógica (o ciclomática) emplearemos una representación del flujo de control denominada grafo de flujo o grafo del programa. Notación de grafo de flujo Cada círculo representa una o más sentencias, sin bifurcaciones. EJEMPLO: ● En primer lugar necesitamos tener claro el diagrama de flujo de la aplicación. ● A continuación se muestra un diagrama de flujo de un programa que determina el mayor de tres valores dados: ● A partir del diagrama de flujo generaremos un GRAFO DE FLUJO para determinar los círculos (nodos) y las flechas (aristas o enlaces) entre ellos. ● Los NODOS representan una o más sentencias. Un solo nodo se puede corresponder con una secuencia de símbolos del proceso y un rombo de decisión. Cuando en una condición aparecen uno o más operadores (condición compuesta), se crea un nodo aparte para cada una de las condiciones. ● Las ARISTAS o ENLACES representan el flujo de control. Una arista termina en un nodo, aunque el nodo no tenga ninguna sentencia procedimental. ● Las áreas delimitadas por aristas y nodos se llaman REGIONES. El área exterior del grafo es otra región más ● Detectamos los nodos que conformarán el grafo de flujo así como los caminos que se pueden recorrer durante la ejecución del programa: Tema 4. Diseño y realización de pruebas Página 12 de 22 ● En el diagrama todas las decisiones tienen que ser simples de forma que hay un nodo para cada una de las condiciones. Un nodo que contiene una condición se llama nodo predicado y de él salen dos o más aristas. ● A continuación dibujamos el grafo de flujo: ● Una vez obtenido el grafo de flujo, tenemos que calcular la complejidad ciclomática, es decir, el número de caminos independientes del conjunto básico de un programa. ● Un camino independiente es cualquier camino del programa que introduce, por lo menos, un nuevo conjunto de sentencias de proceso o una condición, es decir, en términos de diagrama de flujo, está constituido por lo menos por una arista que no haya sido recorrida anteriormente. ● La fórmula para el cálculo de la complejidad ciclomática “V(G)” es: V(G) = aristas – nodos + 2 Otras fórmulas para el cálculo de la complejidad ciclomática son: V(G) = número de regiones del grafo V(G) = nodos predicado + 1 ● Nuestro código tiene una complejidad ciclomática de 4 (13 aristas -11 nodos +2). Eso quiere decir que debemos realizar 4 pruebas para asegurarnos de que cada instrucción se ejecute por lo menos una vez. ● Por último, una vez que conocemos la complejidad ciclomática (4 en nuestro ejemplo), debemos formar los caminos independientes que existen observando el grafo de flujo. Compondremos una tabla empezando por el más corto de los caminos: Tema 4. Diseño y realización de pruebas ● Página 13 de 22 Caso Entrada Salida a>b FALSE b>c TRUE a=3, b=7, c=2 Mayor=b=7 a>b FALSE b>c FALSE a=3, b=4, c=6 Mayor=c=6 a>b TRUE a>c TRUE a=7, b=6, c=2 Mayor=a=7 a>b TRUE a>c FALSE a=5, b=4, c=7 Mayor=c=7 Se establecen los siguientes valores de referencia en función de la complejidad ciclomática: Complejidad ciclomática Evaluación de riesgo Entre 1 y 10 Programas o métodos sencillos, sin mucho riesgo. Entre 11 y 20 Programas o métodos más complejos, riesgo moderado. Entre 21 y 50 Programas o métodos complejos, alto riesgo. Mayor que 50 Programas o métodos no testeables, muy alto riesgo. 6.2 PARTICIÓN O CLASES DE EQUIVALENCIA Es un método de caja negra. Divide los valores de entrada de un programa en clases de equivalencia. Se definen dos tipos de clases de equivalencia: válidas y no válidas. ● ● Ej: Número de empleado numérico, 3 dígitos y >0. ○ Clase de equivalencia válida: número entre 100 y 999 ○ Clase de equivalencia no válida: número menor que 100 Las clases de equivalencia se definen según una serie de directrices: Condiciones de entrada 1. Rango Nº de clases de equivalencia VÁLIDAS 1 clase válida. Cumple rango los valores Nº de clases de equivalencia NO VÁLIDAS 2 clases no válidas. del Un valor por encima del rango Un valor por debajo del rango Tema 4. Diseño y realización de pruebas 2. Valor específico 1 clase válida. Cumple ese valor Página 14 de 22 2 clases no válidas. Un valor por encima Un valor por debajo 3. Miembro de un conjunto 4. Lógica 1 clase válida. 1 clase no válida. Una clase por cada uno de Un valor que no pertenece al conjunto los miembros del conjunto 1 clase válida. Que cumpla la condición 1 clase no válida. Que no cumpla la condición 6.3 ANÁLISIS DE VALORES LÍMITE Se basa en que los errores tienden a producirse con más probabilidad en los límites o extremos de los campos de entrada. Complementa a las clases de equivalencia y los casos de prueba elegidos son los valores justo por encima y por debajo de los márgenes de la clase de equivalencia. También se analizan las condiciones de salida definiendo las clases de equivalencia de salida. Las reglas son las siguientes. Si una condición de entrada especifica: 1. Un rango de valores: diseñar casos de prueba para los límites del rango y para los valores justo por encima y por debajo del rango. Ej. Entrada de valores comprendidos entre 1 y 10 → diseñar pruebas para el valor 1, 10, 0 y 11. 2. Un número de valores (específico): casos de prueba para los valores máximo, mínimo, uno justo encima del máximo y uno justo por debajo del mínimo. Ej. Entrada entre 2 y 10 valores → casos de prueba para 2, 10, 1 y 11 datos de entrada. 3. Aplicar la regla 1 para la condición de salida. Ej. Si hay que aplicar a un campo de salida un descuento entre 10% y 50% → pruebas para 9,99%, 10%, 50% y 50,01%. 4. Usar la regla 2 para la condición de salida. Ej. Si la salida de un programa es una tabla de 1 a 10 elementos → casos de prueba para que produzca una tabla de 0, 1, 10 y 11 elementos. o NOTA: Para las dos reglas anteriores no siempre se podrán generar resultados fuera del rango de salida. 5. Si las estructuras de datos internas tienen límites preestablecidos (por ejemplo un array de 100 elementos), diseñar casos de prueba que ejerciten la estructura de datos en sus límites, primer y último elemento. 7 Herramientas de depuración El proceso de depuración comienza con la ejecución de un caso de prueba. Se evalúan los resultados de la ejecución y se comprueba que hay una falta de correspondencia entre los resultados esperados y los obtenidos. Qué podemos obtener con la depuración: 1. Se encuentra el error, se corrige y elimina. Tema 4. Diseño y realización de pruebas Página 15 de 22 2. No se encuentra, y hay que diseñar nuevos casos de prueba que ayuden a encontrarlo. Al desarrollar un programa cometeremos dos tipos de errores: De compilación: fáciles de corregir ya que el IDE nos informa de la localización del error y cómo solucionarlo. Lógicos: más difíciles de detectar ya que el programa compila bien pero la ejecución devuelve resultados inesperados y erróneos. Son los bugs. El depurador del IDE (Debug) nos permite analizar el código mientras se ejecuta: Poner puntos de ruptura Suspender la ejecución Ejecutar el código paso a paso Examinar el contenido de las variables Elementos más importantes de la depuración (eclipse): Resume (F8): continuar ejecución hasta el siguiente breakpoint. Step Into (F5): si el depurador encuentra una llamada a un método o una función, se irá a la primera instrucción dentro de dicho método. Step Over (F6): no entrará al método sino que irá a la siguiente instrucción. Step Return (F7): si nos encontramos dentro de un método, el depurador sale del método actual. Botón “Watch”: permite crear nuevas expresiones basadas en las variables del programa. Botón “Inspect”: crea una nueva expresión para la variable seleccionada y la agrega a la vista de inspección. 8 Pruebas unitarias JUnit Es una herramienta para realizar pruebas unitarias automatizadas. Integrada en Eclipse Las pruebas unitarias se realizan sobre una clase para probar su comportamiento de modo aislado. Veamos un ejemplo. Vamos a crear una clase de prueba llamada Calculadora: Crear el proyecto Calculadora. Crear la clase Calculadora en el package calculadora. Tema 4. Diseño y realización de pruebas Página 16 de 22 Dentro de la carpeta de proyecto Calculadora crear la carpeta test (New / Source Folder): Una vez tenemos la clase Calculadora creada, hay que crear la clase de prueba CalculadoraTest en la carpeta test haciendo lo siguiente: Tema 4. Diseño y realización de pruebas Seleccionamos la clase Calculadora Pulsamos el botón derecho del ratón New → JUnit Test Case Página 17 de 22 En la siguiente ventana que se nos abrirá: Seleccionaremos New JUnit_Jupiter test, elegimos la carpeta fuente test, package calculadora y dejamos el resto de valores por defecto. En el nombre de la clase por defecto pondrá el nombre de la clase original seguido de “Test”, en nuestro caso “CalculadoraTest”. Tema 4. Diseño y realización de pruebas Pulsaremos NEXT, y deberemos seleccionar los métodos de nuestra clase que queremos probar. Seleccionaremos los cuatro métodos y pulsaremos en FINISH. Página 18 de 22 Tema 4. Diseño y realización de pruebas Página 19 de 22 La clase de prueba se creará automáticamente. Podremos observar lo siguiente: Se crea un método de prueba para cada método seleccionado anteriormente. Los métodos son públicos, y no devuelven ni reciben. El nombre de los métodos incluye la palabra “test”. Sobre cada método aparece “@Test”, que indica al compilador que es un método de prueba. Cada método de prueba tiene una llamada al método fail() con un mensaje indicando que todavía no se ha implementado el método. Este método hace que el test termine con fallo lanzando el mensaje. Debemos conocer algunos métodos propios de JUnit para las comprobaciones: MÉTODOS MISIÓN assertTrue(boolean expresión) Comprueba que la expresión se evalúe a true. assertFalse(boolean expresión) Comprueba que la expresión se evalúe a false. assertEquals(valorEsperado, valorReal) Comprueba que los valores sean iguales. assertNull(Object objeto) Comprueba que el objeto sea null. Tema 4. Diseño y realización de pruebas Página 20 de 22 assertNotNull(Object objeto) Comprueba que el objeto no sea null. assertSame(Object objetoEsperado, Object objetoReal) Comprueba que los objetos sean iguales. assertNotSame(Object objetoEsperado, Object objetoReal) Comprueba que los objetos no sean iguales. fail() Hace que la prueba falle. Cualquiera de los métodos anteriores puede ser llamado pasándole como parámetro, además, un String que será mostrado en caso de que se produzca error. Por ejemplo: assertTrue(String mensaje, boolean expresión) o Si no es true, al producirse el error se verá el mensaje. Veamos un ejemplo: Creamos el test para el método suma y ejecutamos (clic derecho en CalculadoraTest / Run as / JUnit test: Tema 4. Diseño y realización de pruebas Página 21 de 22 Al ejecutar (Run) observamos varias cosas: Que ha pasado bien el test de la suma, puesto que sale con un “tic” verde. Que ha fallado en el resto de métodos, puesto que sale una X con fondo azul. Que no ha habido errores, pues éstos saldrían con una X con fondo rojo. Nos indica que ha ejecutado 4/4 métodos de @Test, ha encontrado 0 Errors y 3 Failures. Implementamos ahora las pruebas unitarias para el resto de métodos, y ejecutamos de nuevo: Para ver la diferencia entre fallo y error, cambiamos el código de dos de los métodos: Para que multiplica() falle, hacemos que el valor esperado no coincida con el resultado; además añadimos un String en el método assertEquals para que lance el mensaje cuando falle. Para que divide() produzca un error, al crear el objeto calculadora asignamos el valor 0 al segundo parámetro (al dividir por 0 se produce una excepción). La traza de ejecución (abajo) nos dice qué ha fallado. Tema 4. Diseño y realización de pruebas Página 22 de 22 El siguiente test comprueba que la llamada al método divide() devuelve la excepción ArithmeticException al dividir 20 entre 0; y por tanto sale por la cláusula catch. Si no se lanza la excepción, se lanza el método fail() con un mensaje. La prueba tiene éxito si se produce la excepción, y falla en caso contrario: