Introducción

Durante y después del proceso de implementación, la aplicación que se está desarrollando debe de ser comprobado para asegurar que satisface su especificación y entrega la funcionalidad esperada por las personas que han pagado por el software (clientes). La verificación y la validación es el nombre dado a estos procesos de análisis y pruebas. La verificación y la validación tienen lugar en cada etapa del proceso del software. La verificación y la validación comienza con revisiones de los requerimientos y continúa con revisiones del diseño e inspecciones de código hasta la prueba del producto.

La validación corresponde con la pregunta: ¿Estamos construyendo el producto correcto?. La verificación corresponde con la pregunta: ¿Estamos construyendo el producto correctamente?.

La función de la verificación implica comprobar que el software está de acuerdo con su especificación. Debería de comprobarse que se satisfacen sus requerimientos funcionales y no funcionales. La función de la validación es un proceso más general. Consiste en asegurar que el sistema software satisfaga las expectativas del cliente.

El último objetivo del proceso de verificación y validación es establecer la seguridad de que el sistema software está hecho para un propósito. Es decir, el sistema debe de ser lo suficientemente bueno para su uso pretendido. El nivel de confianza requerido depende del próposito del sistema, las expectativas de los usuarios del sistema y el entorno de mercado actual del sistema.

La verificación y validación es un proceso caro. Es necesaria una planificación cuidadosa para obtener el máximo provecho de las inspecciones y pruebas y controlar los costes del proceso de verificación y validación.

Dentro del proceso de verificación y validación, existen dos aproximaciones complementarias para el análisis y comprobación de los sistemas:

  1. Las inspecciones de software analizan y comprueban las representaciones del sistema tales como el documento de requerimientos, lso diagramas de diseño y el código fuente del programa. Puede usarse las inspecciones en todas las etapas del proceso. Las inspecciones pueden ser complementadas con algún tipo de análisis automático del código fuente de un sistema o de los documentos asociados. Las inspecciones de software y los análisis automáticos son técnicas de verificación y validación estáticas, ya que no se necesita ejecutar el software en una computadora.
  2. Las pruebas del software implican ejecutar una implementación del software con datos de prueba. Se examinan las salidas del software y su entorno operacional para comprobar que funciona tal y como se requiere. Las pruebas son una técnica dinámica de verificación y validación.

Las revisiones de los requerimientos y del diseño son las principales técnicas utilizadas para la detección de errores en el diseño y la especificación.

Solo puede probarse un sistema cuando está disponible un prototipo o una versión ejecutable del programa. Una ventaja del desarrollo incremental es que una versión probable del sistema está disponible en etapas tempranas del proceso de desarrollo. Las funcionalidades pueden probarse a medida que se van añadiendo al sistema, por lo que no tiene que realizarse una implementación completa antes de que comiencen las pruebas.

Las técnicas de inspección comprenden las inspecciones de programas, el análisis automático del código fuente y la verificación formal. Sin embargo, las técnicas estáticas sólo pueden comprobar la correspondencia entre un programa y su especificación (verificación). No pueden demostrar que el software es operacionalmente útil. Tampoco se pueden utilizar técnicas estáticas para comprobar las propiedades emergentes del software tales como su rendimiento y fiabilidad.

La prueba de programas siempre será la principal técnica de verificación y validación. Las pruebas implican ejecutar el programa utilizando datos similares a los datos reales procesados por el programa. Los defectos de los programas se descubren examinando las salidas del programa y buscando las anomalías. Existen dos tipos de pruebas que pueden utilizarse en diferentes etapas del proceso del software.

  1. Las pruebas de validación intentan demostrar que el software es el que el cliente quiere (satisface sus requerimientos). Se pueden utilizar pruebas estáticas para probar el rendimiento y la fiabilidad de los programas y para probar cómo trabaja en ciertas condiciones operacionales.
  2. Las pruebas de defectos intentan revelar defectos en el sistema en lugar de simular su uso operacional. El objetivo de las pruebas de defectos es hallar inconsistencias entre un programa y su especificación.

No existe un límite definido entre las aproximaciones de pruebas. Durante las pruebas de validación, se encontrarán defectos en el sistema. Durante las pruebas de defectos, alguno de los tests mostrará que el programa satisface sus requerimientos.

Normalmente, los procesos de verificación y validación y depuración se intercalan. A medida que se descubren defectos en el programa que se está probando, tiene que cambiarse éste para corregir tales defectos. Sin embargo, las pruebas de verificación y validación y la depuración tienen diferentes objetivos:

  1. Los procesos de verificación y validación intentan establecer la existencia de defectos en el sistema software.
  2. La depuración es un proceso que localiza y corrige estos defectos.

No existen métodos sencillos para la depuración de programas. Los depuradores habilidosos buscan patrones en las salidas de las pruebas en donde se ponen de manifiesto los defectos y utilizan su conocimiento sobre el tipo de defecto, el patrón de salida, el lenguaje de programación y el proceso de programación para localizar el defecto. Durante el proceso de depuración, puede utilizarse el conocimiento de errores comunes de programación (por ejemplo, olvidar incrementar un contador) y hacer corresponder éstos con los patrones observados.

Localizar los defectos en un programa no siempre es un proceso sencillo, ya que el defecto puede no estar cerca del punto en el que falló el programa. Para localizar un defecto de un programa, se puede tener que diseñar pruebas adicionales que reproduzcan el defecto original y que determinen con precisión su localización en el programa. Se puede tener que hacer manualmente una traza del programa, línea por línea. Las herramientas de depuración que recopilan información sobre la ejecución del programa también pueden ayudar a localizar la fuente de un problema.

Las herramientas de depuración interactivas generalmente forman parte de un conjunto de herramientas de soporte del lenguaje que se integran con un sistema de compilación. Éstas proporcionan un entorno de ejecución especializado para el programa que permite acceder a la tabla de símbolos del compilador y, desde aquí, a los valores de las variables del programa. Se puede controlar la ejecución «paso a paso» del programa sentencia por sentencia. Después de ejecutar cada sentencia, pueden examinar los valores de las variables y así se puede descubrir la localización del defecto.

Cuando se ha descubierto un defecto en el programa, hay que corregirlo y volver a validar el sistema. Esto puede implicar volver a inspeccionar el programa o hacer pruebas de regresión en las que se ejecutan de nuevo los tests existentes. Las pruebas de regresión se utilizan para comprobar que los cambios en el programa no introducen nuevos defectos. La experiencia ha demostrado que una alta proporción de reparaciones de defectos son incompletas o bien introducen nuevos defectos en el programa.

En principio, deberían repetirse todos los tests después de la reparación de cada defecto. En la práctica, esto normalmente supone un coste demasiado elevado. Como parte del plan de pruebas, deberían identificarse dependencias entre los componentes y las pruebas asociadas con cada componente. Esto es, debería de poderse establecer una traza entre los casos de prueba y los componentes que son probados. Si esta trazabilidad se documenta, entonces se puede ejecutar un subconjunto de los casos de prueba del sistema para comprobar el componente modificado y sus dependencias.

Los principales componentes de un plan de pruebas para un sistema grande y complejo son:

  1. El proceso de prueba: una descripción de las principales fases del proceso de prueba.
  2. Trazabilidad de requerimientos: Los usuarios son los más interesados en que el sistema satisfaga sus requerimientos y las pruebas deberían planificarse para que todos los requerimientos se prueben individualmente.
  3. Elementos probados: Deberían de especificarse los elementos del proceso del software que deberían de ser probados.
  4. Calendario de pruebas: Un calendario de todas las pruebas y la asignación de recursos para este calendario se enlaza, obviamente, con la agenda general del desarrollo del proyecto.
  5. Procedimientos de registro de las pruebas: No es suficiente ejecutar simplemente las pruebas; los resultados de las pruebas deben ser registrados sistemáticamente. Debe ser posible auditar el proceso de pruebas para comprobar que se ha llevado a cabo correctamente.
  6. Requerimientos hardware y software: Esta sección debería determinar las herramientas software requeridas y la utilización del hardware.
  7. Restricciones: En esta sección deberían anticiparse las restricciones que afectan al proceso de pruebas como la escasez personal.

Para sistemas más pequeños, se pueden utilizar un plan de pruebas menos formal.

Los planes de pruebas no son doumentos estáticos, sino que evolucionan durante el proceso de desarrollo. Los planes de pruebas cambian debido a retrasos en otras etapas del proceso de desarrollo. Si parte de un sistema está incompleto, el sistema no puede probarse como un todo. Entonces tiene que revisarse el plan de pruebas para volver a desplegar y a asignar a los encargados de las pruebas a alguna otra actividad, y recuperalos cuando el software vuelva a estar disponible.

Inspecciones del software:

Las inspecciones del software son un proceso de verificación y validación en el que un sistema software se revisa para encontrar errores, omisiones y anomalías. Generalmente, las inspecciones se centran en el código fuente, pero puede inspeccionarse cualquier representación legible del software como los requerimientos o un modelo de diseño. Cuando se inspecciona un sistema, se utiliza conocimiento del sistema, su dominio de aplicación y el lenguaje de programación o modelo de diseño para descubrir errores. Las inspecciones son una forma de análisis estático (se examina el programa sin ejecutarlo).

Existen 3 ventajas fundamentales de la inspección sobre las pruebas:

  1. Durante las pruebas, los errores pueden ocultar otros errores. Cuando se descubre un error, nunca se puede estar seguro de si otras anomalías de salida son debidas a un nuevo error o son efectos laterales del error original. Debido a que la inspección es un proceso estático, no hay que preocuparse de las interacciones entre errores. Por lo tanto, una única sesión de inspección puede descubrir muchos errores en un sistema.
  2. Pueden inspeccionarse versiones incompletas de un sistema sin costes adicionales.
  3. Una inspección puede considerar atributos de calidad más amplios de un programa tales como grado de cumplimiento con los estándares, portabilidad y mantenibilidad. Puede buscarse ineficiencias, algoritmos no adecuados y estilos de programación que podrían hacer que el sistema fuese difícil de mantener y actualizar.

Las inspecciones sobrecargan al inicio los costos de verificación y validación del software y conducen a un ahorro de costes sólo después de que los equipos de desarrollo adquieran experiencia en su uso. También hay problemas prácticos en cuanto a la organización de las inspecciones, ya que estas requieren tiempo para organizarse y parecen ralentizar el proceso de desarrollo.

Las inspecciones de programas son revisiones cuyo objetivo es la detección de defectos en el programa. Es un método muy usado en verificación de programas. Se basa en que un grupo con miembros, que tienen diferentes conocimientos, realizan una revisión cuidadosa línea por línea del código fuente del programa.

La diferencia principal entre las inspecciones de programas y otros tipos de revisiones de calidad es que el objetivo primordial de las inspecciones es encontrar defectos en el programa en lugar de considerar cuestiones de diseño más generales. Los defectos pueden ser errores lógicos, anomalías en el código que podrían indicar una condición errónea, el incumplimiento de estándares del proyecto o de la organización. Otros tipos de revisión pueden estar más relacionados con la agenda, los costes, el progreso frente a hitos definidos o la evolución de si es probable que el software cumpla los objetivos fijados por la organización.

La inspección de programas es un proceso formal realizado por un equipo. Los miembros del equipo analizan sistemáticamente el código y señalan posibles defectos.

Antes de que comience una inspección del programa, es necesario que:

  1. Se tenga una especificación precisa del código a inspeccionar. Es imposible inspeccionar un componente a un nivel de detalle requerido para detectar defectos sin una especificación completa.
  2. Los miembros del equipo de inspección estén familiarizados con los estándares de la organización.
  3. Se haya distribuido una versión compilable y actualizada del código a todos los miembros del equipo. No existe ninguna razón para inspeccionar código que esté "casi completo" incluso si un retarso provoca desfases en la planificación.

Las posibles comprobaciones que se deben de realizar durante un proceso de inspección son las siguientes:

  1. Defectos de datos: ¿Se inicializan todas las variables antes de que se utilicen sus valores? ¿Tienen nombre todas las constantes? ¿El límite superior de los vectores es igual al tamaño del vector? Si se utilizan cadenas de caracteres, ¿tienen un delimitador explícitamente asignado? ¿Existe posibilidad de que el búfer se desborde?.
  2. Defectos de control: Para cada sentencia de condicional, ¿es correcta la condición? ¿Se garantiza que termina cada bucle? ¿Están puestas correctamente entre llaves las sentencias compuestas? En las sentencias case, ¿se tienen en cuenta todos los posibles casos? Si se requiere una sentencia break después de cada caso en las sentencias case, ¿se ha incluido?
  3. Defectos de entrada/salida: ¿Se utilizan todas las variables de entrada? ¿Se les asigna un valor a todas las variables de salida? ¿Pueden provocar corrupciones de datos las entradas no esperadas?
  4. Defectos de interfaz: ¿Las llamadas a funciones y a métodos tienen el número correcto de parámetros? ¿Concuerdan los tipos de parámetros reales y formales? ¿Están en el orden correcto los parámetros? Si los componentes acceden a memoria compartida, ¿tienen el mismo modelo de estructura de la memoria compartida?
  5. Defectos de gestión de almacenamiento: Si una estructura enlazada se modifica, ¿se reasignan correctamente todos los enlaces? Si se utiliza almacenamiento dinámico, ¿se asigna correctamente el espacio de memoria? ¿Se desasigna explícitamente el espacio de memoria cuando ya no se necesita?
  6. Defectos de manejo de excepciones: ¿Se tienen en cuenta todas las condiciones de error posibles?

Otro análisis estático:

Existen otro tipo de errores u anomalías que son a menudo el resultado de errores de programación u omisiones, de forma que resalten aspectos del programa que podrían funcionar mal. Estas anomalías no son necesariamente defectos del programa. Pueden ser deliberadas o pueden no tener consecuencias adversas:

  1. Defectos de datos: Variables utilizadas antes de su inicialización, variables declaradas pero nunca utilizadas, variables asignadas dos veces pero nunca utilizadas entre asignaciones, posibles violaciones de los límites de los vectores, variables no declaradas.
  2. Defectos de control: Código no alcanzable, saltos incondiciones en bucles.
  3. Defectos de entrada/salida: Las variables salen dos veces sin intervenir ninguna asignación.
  4. Defectos de interfaz: Inconsistencias en el tipo de parámetros, inconsistencias en el número de parámetros, los resultados de las funciones no se utilizan, existen funciones y procedimientos a los que no se les llama.
  5. Defectos de gestión de almacenamiento: Punteros sin asignar, aritmética de punteros.

Las etapas implicadas en el análisis estático comprenden:

  1. Análisis del flujo de control: Esta etapa identifica y resalta bucles con múltiples salidas o puntos de entrada y código no alcanzable.
  2. Análisis del uso de datos: Esta etapa revela como se utilizan las variables del programa.
  3. Análisis de interfaces: Este análisis comprueba la consistencia de las declaraciones de funciones y procedimientos y su utilización (en caso de JAVA no es necesario ya que es un lenguaje fuertemente tipado y el compilador es quien se encarga de estas comprobaciones).
  4. Análisis del fujo de información: Esta fase del análisis identifica las dependencias entre las variables de entrada y salida.
  5. Análisis de caminos: Esta fase del análisis semántico identifica todos los posibles caminos en el programa y muestra las sentencias ejecutadas en dicho camino.

Pruebas del software:

El objetivo de las pruebas de software se basan en establecer que el sistema satisface sus requerimientos funcionales y no funcionales, y que no se comporta de forma inesperada.

Dichas pruebas se componen de distintas categorías, que tratamos a continuación.

Pruebas del sistema:

Las pruebas del sistema implican integrar dos o más componentes que implementan funciones del sistema o características y a continuación se prueba este sistema integrado. En un proceso de desarrollo iterativo, las pruebas del sistema se ocupan de probar un incremento que va a ser entregado al cliente; en un proceso en cascada, las pruebas del sistema se ocupan de probar el sistema completo.

Existen tres fases distintas de pruebas del sistema:

  1. Pruebas de integración: el equipo de pruebas tiene acceso al código fuente del sistema. Cuando se descubre un problema, el equipo de integración intenta encontrar la fuente del problema e identificar los componentes que tienen que ser depurados. Las pruebas de integración se ocupan de encontrar defectos en el sistema.
  2. Pruebas de entregas: se prueba una versión del sistema que podría ser entregada a los usuarios. Aquí, el equipo de pruebas se ocupa de validar que el sistema satisface sus requerimientos y con asegurar que el sistema es confiable. Las pruebas de entregas son normalmente pruebas de caja negra en las que el equipo de pruebas se ocupa simplemente de demostrar si el sistema funciona o no correctamente. Los problemas son comunicados al equipo de desarrollo cuyo trabajo es depurar el programa. Si la entrega es lo suficientemente buena, el cliente puede entonces aceptarla para su uso.
  3. Pruebas de rendimiento: Las pruebas de rendimiento tienen que diseñarse para asegurar que el sistema pueda procesar su carga esperada. Esto implica planificar una serie de pruebas en las que la carga se va incrementando regularmente hasta que el rendimiento del sistema se hace inaceptable. Este tipo de fallos tienen 2 funciones: prueba el comportamiento de fallo de ejecución del sistema y sobrecargan el sistema y pueden provocar que se manifiesten defectos que normalmente no serían descubiertos.

Pruebas de entregas:

Las pruebas de entregas son el proceso de probar una entrega del sistema que será distribuida a los clientes. El principal objetivo de este proceso es incrementar la confianza del suministrador en que el sistema satisface sus requerimientos. Si es así, este puede entregarse como un producto o ser entregado al cliente. Para demostrar que el sistema satisface sus requerimientos, tiene que mostrarse que éste entrega la funcionalidad especificada, rendimiento y confiabilidad, y que no falla durante su uso normal.

Las pruebas de entregas son normalmente un proceso de pruebas de caja negra en las que las pruebas se derivan a partir de la especificación del sistema. El sistema se trata como una caja negra cuyo comprotamiento sólo puede ser determinado estudiando sustentradas y sus salidas relacionadas.

EL objetivo es seleccionar entradas que tienen una alta probabilidad de generar fallos de ejecución del sistema. Algunos ejemplos son los siguientes:

  1. Elegir entradas que fuerzan a que el sistema genere todos los mensajes de error.
  2. Diseñar entradas que hacen que los búferes de entrada se desborden.
  3. Repetir la misma entrada o series de entradas varias veces.
  4. Forzar a que se generen las salidas inválidas.
  5. Forzar los resultados de los cálculos para que sean demasiado grandes o demasiado pequeños.

Para cada una de estas pruebas, debería diseñarse un conjunto de pruebas que incluyan entradas válidas e inválidas y que generen salidas válidas e inválidas. También deberían organizarse pruebas basadas en escenarios para que los escenarios más probables sean probados primero, y los escenarios inusuales o excepcionales sean probados más tarde, de forma que el esfuerzo se centre en aquellas partes del sistema que reciben un mayor uso.

Los casos de uso y los diagramas de secuencia asociados pueden ser una base para las pruebas del sistema. Pueden usarse ambos durante la integración y pruebas de entregas.

Pruebas de componentes:

Las pruebas de componentes (de unidad) son el proceso de probar los componentes individuales en el sistema. Éste es un proceso de pruebas de defectos, por lo que su objetivo es encontrar defectos en estos componentes.

Exiten diferentes tipos de componentes que pueden probarse en esta etapa:

  1. Funciones individuales o métodos dentro de un objeto.
  2. Clases de objetos que tienen varios atributos y métodos.
  3. Componentes compuestos formados por diferentes objetos o funciones. Estos componentes compuestos tienen una interfaz definida que se utiliza para acceder a su funcionalidad.

Las funciones o métodos individuales son el tipo más simple de componente y sus pruebas son un conjunto de llamadas a estas rutinas con diferentes parámetros de entrada.

Las pruebas de clases de objetos deberían de incluir las pruebas aisladas de todas las operaciones con el objeto, la asignación y consulta de todos los atributos asociados con el objeto, y ejecutar el objeto en todos sus posibles estados. Esto significa que deben simularse todos los eventos que provocan un cambio de estado en el objeto.

Siempre que una superclase proporcione operaciones que son heredadas por varias subclases, todas estas subclases deberían ser probadas con todas las operaciones heredadas. Ya que la operación heredada puede hacer suposiciones sobre otras operaciones y atributos, que pueden haber cambiado cuando se han heredado. Del mismo modo, cuando una operación de una superclase es sobrescrita, entonces la nueva operación debe de ser probada.

Pruebas de interfaces:

Los objetos y componentes se definen por sus interfaces y pueden ser reutilizados en combinación con otros componentes en sistemas diferentes. Los errores de interfaz en el componente compuesto no pueden detectarse probando los objetos individuales o componentes. Los errores en el componente compuesto pueden surgir debido a interacciones entre sus partes.

Los errores de interfaces son una de las formas más comunes de error en sistemas complejos. Estos errores se clasifican:

  1. Mal uso de la interfaz: Un componente llama a otro componente y comete un error en la reutilización de su interfaz.
  2. No compresión de la interfaz: El componente que realiza la llamada no comprende la especificación de la interfaz del componente al que llama, y hace suposiciones sobre el comportamiento del componente invocado. El componente invocado no se comporta como era de esperar y esto provoca un comportamiento inesperado en el componente que hace la llamada.
  3. Errores temporales: Se producen en sistemas de tiempo real que utilizan una memoria compartida o una interfaz de paso de mensajes.

Volver a pruebas - verificación y validación I

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-Share Alike 2.5 License.