|
Probando
software y números de versión.
Cuando aparecieron los primeros grandes sistemas
informáticos se incluyo a nivel metódico e imprescindible
un hasta entonces nuevo proceso en la confección de los mismos:
el proceso de prueba.
Hoy en día se calcula que la fase o proceso
de pruebas representa más de la mitad del coste de un programa,
ya que requiere un tiempo similar al de la programación lo
que obviamente acarrea un alto costo económico cuando estos
no involucran vidas humanas, puesto que en este último caso
el costo suele superar el 80% siendo esta etapa mas cara que el
propio desarrollo y diseño de los distintos programas que
conforman el sistema.
Un proceso de pruebas requiere mucho más
que tiempo y dinero, necesita una verdadera metodología la
cual exige herramientas y conocimientos destinados a dicha tarea,
este texto trata de ser una pequeña guía para los
programadores que aún no se han entrenado en este plano,
no obstante recomiendo la lectura del libro The art of software
testing del autor Glenford J. Myers en el cual se explican en un
nivel muy superior muchos de los puntos que más abajo detallo,
desconozco si existen traducciones al español, la copia que
yo poseo está en inglés.
Hacer la prueba infalible de un programa implicaría ponerlo
en todas las situaciones posibles, de esta manera aseguraríamos
que el mismo se encuentra completamente libre de errores, como se
imaginarán esto es imposible porque a pesar de que el número
de líneas que lo conforman es finito, la prueba pasa a ser
infinita cuando entran en juego los bucles con lo que hacer la prueba
empírica exacta pasa de ser una enorme e incalculable cantidad
de posibilidades a una cifra ciertamente infinita. Considerando
este último punto lo que queda por hacer es buscar formas
y métodos abordables para acercarse lo más posible
a un resultado optimo.
La etapa de pruebas no debe ser posterior a la
confección de un programa, tiene que ser paralela a la programación
y como bien dice Glenford Myers en algunos casos deberá ser
anterior, primero probar y después programar. Yo me sorprendí
como lo habrán hecho ustedes al leer esto, pero luego leí
algo que me aclaro un poco el tema, Glenford preguntaba, ¿O
acaso nunca desarrollaste una rutina pensando, esto hay que probarlo?.
La versión es algo que nos ayudará
mucho a la hora de encontrar fallas, es importante como un método
de organización que cada vez que se modifique algo de un
programa se modifique la versión del mismo y que esta figure
en un lugar legible incluso para el cliente.
Particularmente utilizo el tipo de incremento de
versión que plantea Roger Pressman, por haberlo encontrado
muy útil para la clasificación de las mismas.
El método de Pressman es el siguiente: propone
que el número de versión esté compuesta por
cuatro partes, tres numéricas y una letra del alfabeto griego
que puede no estar en las versiones finales.
El primer número antes del punto corresponde a la cantidad
de veces que el programa ha sido reprogramado desde cero, es decir
cada vez que se hace un programa nuevo que posea el mismo nombre
y funciones que su antecesor.
Los número detrás del punto indican la cantidad de
modificaciones realizadas a pedido y por corrección de fallas
respectivamente.
Por ejemplo, una vez que se haya terminado el programa su versión
será 1.0, si luego se le hace una modificación por
pedido del cliente o para mejorar algún aspecto del sistema
la versión será incrementada a 1.1.
Suponiendo que más tarde el programador detecta y corrige
dos errores la versión será 1.12
Cuando algún numero de la sub-versión se vea incrementado
por encima de 9 este será separado por puntos quedando así
bien delimitada la versión principal, la cantidad de modificaciones
por pedido y la cantidad de corrección de errores.
Ejemplo:
V1.39 - Representa 3 modificaciones a pedido y 9 correcciones de
errores
V1.03.10 - Representa 3 modificaciones a pedido y 10 correcciones
de errores
La letra que se coloque después de la versión
indicará en que etapa de prueba este se encuentre, usando
alfa para las pruebas en las que el cliente, sea este interno o
externo, se encuentra en el área de desarrollo cosa muy común
en las empresas que a pesar de no dedicarse a la comercialización
de software tienen su propio equipo de desarrollo y programación.
La letra "Beta" se utilizará para
indicar que el programa se encuentra en una etapa de prueba beta
o "Beta test", esto será cuando el software se
encuentre en el entorno del cliente sin el seguimiento del desarrollador,
y será este quien oportunamente informe las fallas detectadas.
V1.03.10B - Representa 3 modificaciones a pedido
y 10 correcciones de errores y que aún se encuentra en fase
de prueba "Beta".
Cuando la versión carezca de letra del alfabeto
griego indicará que la versión es final, o sea que
fue probada y revisada y está libre de errores conocidos,
a menos que el proveedor de software indique lo contrario.
Este último punto se presta a una confusión
muy común, es obvio que cuando entregamos un programa terminado
la idea es demostrar que funciona y cumple con los requisitos y
necesidades del cliente, esto es muy distinto si el programa se
encuentra en fase de pruebas beta, la idea de las pruebas beta no
es demostrar que el sistema no tiene fallas, por el contrario trata
de descubrir las fallas que no han sido detectadas por el equipo
de desarrollo, este punto hay que tenerlo muy en claro.
Las pruebas beta como anteriormente mencioné
son pruebas funcionales sobre el sistema completo y buscan una cobertura
de la especificación de requisitos, es común que luego
de los mas cuidadosos testeos por parte del desarrollador el software
contenga "vicios ocultos" que solo serán notados
cuando el o los clientes comiencen a darle el uso para el cual el
programa ha sido pensado, en algunas oportunidades el cliente puede
encontrar fallas causadas por usar el programa de una manera para
la cual no había sido pensado, muchos de ustedes entenderán
a que me refiero, en estos casos es normal decir cosas como "¿Como
puede ser que a alguien se le ocurra hacer tal cosa?", en estos
casos se puede decir que los requisitos no están claros o
son ambiguos, cosa que puede salvar la cara pero que de ninguna
manera dejará conforme al cliente. Decir que el usuario es
la falla del sistema es otra tentación que conviene reprimir.
Las pruebas a realizar en tiempo de desarrollo.
Pruebas informales o fase de prueba informal, son
aquellas pruebas que hace el desarrollador en su oficina, tienen
como objetivo comprobar que el programa compile y ver que todo está
yendo como debiera, normalmente se realizan varios cientos de estas
pruebas que básicamente consisten en compilar periódicamente
durante el desarrollo y ejecutar para ver el resultado.
Dentro de las pruebas en tiempo de desarrollo encontraremos
las pruebas de unidades, estas son pruebas de menor escala y consisten
en probar cada uno de los módulos que conforman el programa,
cuando estos módulos son extensos o complejos se dividen
para probar objetivamente partes mas pequeñas, este tipo
de pruebas es la mas común.
Las pruebas de integración tienen por objetivo
verificar el conjunto funcionamiento de dos o mas módulos,
si bien se deben poner en práctica desde la creación
de dos módulos que interactúen entre si, en el supuesto
caso que se necesiten mas de dos módulos para efectuar las
pruebas, deberán generarse simples emuladores de módulos
que entreguen datos esperados para la prueba individual de cada
uno.
También las pruebas de integración
pueden ser realizadas en forma ascendente, esto evita tener que
crear módulos emuladores, ya que a medida que se va creando
la pirámide va siendo probada de abajo hacia arriba (Down
to Top), como se imaginaran esto acarrea un trabajo simétricamente
mayor lo que equipara o supera el tiempo que podría tomar
el crear módulos para prueba.
Las pruebas después de la programacion.
Cuando se considera que un módulo está
terminado se realizan las pruebas sistemáticas, el objetivo
de estas es buscar fallos através de un criterio específico,
estos criterios se denominan "pruebas de caja negra y de caja
blanca".
Las pruebas de caja negra son aquellas que se enfocan
directamente en el exterior del módulo, sin importar el código,
son pruebas funcionales en las que se trata de encontrar fallas
en las que este no se atiene a su especificación, como ser
interfaz con el usuario, apariencia de los menús, control
de las teclas, etcétera.
Este tipo de pruebas no es aplicable a los módulos que trabajan
en forma transparente al usuario.
Para realizar estas pruebas existe una técnica algebraica
llamada "clases de equivalencia", consiste en tratar a
todos las posibles entradas y parámetros como un modelo algebraico,
y utilizar las clases de este modelo para probar un amplio rango
de posibilidades.
Para la generación de estas clases no se puede armar un modelo,
pero se pueden seguir las siguientes pautas como guía utilizable
para la creación de cada clase.
Por ejemplo:
Cuando una entrada es booleana, existen solo dos clases, verdadero
o falso.
Para una entrada que está comprendida dentro de un rango,
existen tres clases, por debajo, dentro, y por encima del rango.
Utilizando este ejemplo se pueden generar las distintas clases aplicables
al módulo en cuestión, luego, se procede a ingresarle
al módulo un valor de cada clase.
Las pruebas de caja blanca son mucho mas amplias,
normalmente se denominan pruebas de cobertura o pruebas de caja
transparente, al total de pruebas se caja blanca se le llama cobertura,
la cobertura es un número porcentual que indica cuanto código
del programa se ha probado.
Básicamente la idea de pruebas de cobertura consiste en diseñar
un plan de pruebas en las que se vaya ejecutando sistemáticamente
el código hasta que haya corrido todo o la gran mayoría
de el, esto que parece complicado es mas aún cuando el programa
contiene código de difícil alcance, como por ejemplo
manejadores de errores o "código muerto".
Entiéndase por código muerto a aquellas funciones
y/o procedimientos que hemos incluido por encontrarse en recopilaciones
pero que estas nunca son ejecutadas por el programa, estas funciones
no necesariamente deberán ser removidas pero si probadas
por si algún día en revisiones futuras son incluidas.
Para los módulos que no poseen condiciones basta con ejecutar
una vez el programa para asegurar una cobertura total.
Es importante que el diseño de cobertura sea eficiente y
lo menos redundante posible, por ejemplo, en el siguiente código:
If Variable_Booleana
..Do Modulo_X
EndIf
Como no hay un "else", a simple vista
con ejecutar una vez con éxito la condición bastaría,
en términos de cobertura es así, pero entendiendo
que el "Modulo_X" podría modificar variables o
valores que afecten a la ejecución del resto del código
habría que ejecutar 2 veces la condición, una satisfaciendo
y otra no.
Respecto al siguiente ejemplo:
If Variable_Booleana1 .Or. Variable_Booleana2
..Do Modulo_X
EndI
O este otro:
If Variable_Booleana1 .And. Variable_Booleana2
..Do Modulo_X
EndI
A simple vista y considerando que ambas variables pueden tener 2
valores se precisarían 4 pruebas para realizar la cobertura,
pero esto no es así, solo es necesario 2 pruebas en el caso
que el Modulo_X pueda interferir de alguna manera en el programa
o una sola satisfaciendo la condición si el Modulo_X no alterara
de ninguna manera con el resto de los procedimientos y condiciones
a ejecutarse.
Probado las 4 posibilidades solo estaríamos probando que
funcione el comando "IF" en sí, lo cual ya fue
probado por el desarrollador del lenguaje de programación.
Con respecto a la cobertura en bucles el tema es
un poco mas delicado, a simple vista un bucle no es mas que un salto
condicional que se repite hasta que se cumpla o deje de cumplirse
una o mas condiciones, en teoría esto es simple, pero en
la práctica son una fuente inagotable de versátiles
errores, que en su gran mayoría suelen ser catastróficos.
En primer lugar, la cantidad de veces que se ejecute un bucle debe
ser precisa, y todos los programadores saben que no es difícil
equivocarse y programar un bucle que se ejecute una vez de mas o
una vez de menos, siempre que esto suceda los resultados serán
indeseables, y muchas veces cuando se trate de manejos de datos
complicados de calcular no será fácil advertir el
error, el cual será caro cuando se trate de valores que se
utilizan para tomar determinaciones a nivel empresarial o involucren
vidas humanas.
Para realizar la cobertura total de un bucle se necesitan 3 pruebas,
cero ejecuciones, una ejecución y mas de una ejecución.
Los bucles de tipo "for", parecerían ser mas sencillos,
ya que la cantidad de ejecuciones es definida por su cabecera y
controlada por el compilador, con una ejecución bastaría
para una cobertura total, siempre y cuando no contengan código
que altere el valor de la variable de control o comandos de salida
(Exit), en este caso requiere un examen un poco mas detallado ya
que el bucle deja de ser responsabilidad del lenguaje compilador
y pasa a ser del programador.
Particularmente aconsejo que no se utilicen bucles "for"
modificando su variable de control o incluyendo en ellos comandos
de salida.
Los
En pocas palabras es muy importante diseñar
lo mas precisamente posible las pruebas de cobertura, para que quede
en lo posible la mayor parte del código probado con la mínima
cantidad de pruebas realizadas.
Hay que tener en cuenta dos puntos importantes,
en primer lugar las pruebas de caja blanca no reemplazan, solo complementan
a las de caja negra y de aceptación, y en segundo lugar,
las pruebas de cobertura deben ser realizadas una ves terminado
el software y no deben ser confundidas con las pruebas informales
que realiza el programador en momentos de desarrollo, dado que si
bien estas van cubriendo distintos fragmentos de cada módulo,
nunca son eficaces por no tener un diseño apropiado.
El valor porcentual de pruebas de cobertura de
un sistema terminado nunca deberá ser inferior al 51%, y
elevándose en función al coste que podría ocasionar
las fallas posibles, ascendiendo a un 99% cuando estén involucradas
vidas humanas o cuando la falla no da una segunda oportunidad.
El uso de un depurador es muy útil en las
pruebas de cobertura, ya que se pueden ir viendo todas las líneas
y ejecuciones paso a paso, esto no muy práctico y es bastante
tedioso, pero es considerablemente efectivo.
Pruebas de aceptación, son las que hará
el cliente, en esta fase de pruebas se determina que el sistema
cumple con el objetivo deseado, determina la conformidad del cliente
antes de que el programa le sea entregado como una versión
final.
Las pruebas conocidas con el nombre de pruebas
de rendimiento son aquellas que determinan los tiempos de respuesta,
el espacio que ocupa el módulo en disco o en memoria, el
flujo de datos que genera a través de un canal de comunicaciones,
etc..
Pruebas de transformación, este método
curioso y caro aún se pone en funcionamiento por diversas
empresas, consiste en dividir el equipo de desarrollo en dos partes
una vez realizadas todas las pruebas y corregidos todos los errores,
luego una de las dos partes introduce pequeños errores en
el sistema y la otra parte debe encontrarlos con los mismos procedimientos
que se usaron para buscar los errores nativos.
Esto es muy costoso y consume grandes cantidades de tiempo.
Pruebas de robustez, comúnmente denominadas
"robustness test" son las encargadas de verificar la capacidad
del programa para soportar entradas incorrectas, por ejemplo en
un sistema de facturación donde el usuario debe ingresar
códigos de productos y luego cantidades es mas que factible
que en algún momento ingrese un código en el campo
de cantidad, si el programa fue sometido a pruebas de robustez este
valor sería rechazado o grabado como una cantidad inmensa
pero que no daría error por desbordamiento de datos.
Las denominadas pruebas de resistencia se utilizan
para saber hasta donde puede soportar el programa condiciones extremas,
por ejemplo los tiempos de respuesta con el procesador a un 95%
de su utilidad o con muy poco espacio en disco.
El plan de pruebas
Un plan de pruebas deberá cumplir con ciertos
puntos, en primer lugar se deberá tener en claro que tipo
de pruebas se van a aplicar y su correcto orden y diseño.
Diseñar correctamente las pruebas de caja blanca y caja negra
es un punto crucial.
Antes de que las pruebas comiencen deberá estar aclarado
cual será el punto aceptable de cobertura como así
también cual será el método para medir los
resultados.
Las pruebas informales donde el resultado se aprecia muy sutilmente
no deberán ser parte del plan de pruebas.
Recordar siempre que una prueba solo tiene éxito cuando el
programa falla.
Como realizo las pruebas de un programa
que me piden que esté terminado ayer.
En la empresa para la cual actualmente trabajo
es muy común que se me solicite soluciones, nuevos programas,
modificaciones a módulos, cambios de todo tipo, ya sea a
nivel datos, código o interfaz con el usuario.
Estas solicitudes se realizan en el mimo momento en que surge la
necesidad de cubrir un área que hasta entonces no era imprescindible,
y normalmente deben estar en el día o en un lapso de horas,
a menos que se trate de verdaderos desarrollos que se planifican
con anterioridad y toman unos cuantos meses.
En estas oportunidades el único tipo de prueba que se puede
realizar son las informales, las que voy haciendo a medida que confecciono
el código, y cuando este aparenta estar listo tiene que ser
puesto en funcionamiento.
Obviamente mis clientes, internos en este caso, no soportan ver
un error y no se conforman con un "no tuve tiempo de probar
en la totalidad", por lo que las fallas del programa no serán
nada gratas para mi.
En este caso lo que hago es desarrollar y utilizar las pruebas informales
y alguna que otra prueba de integración, y una vez puesto
en funcionamiento el producto procedo con las pruebas de caja blanca,
ya que las de caja negra se están haciendo simultáneamente
por verdaderos clientes.
Por suerte poseo dos servidores uno funcional y otro que es una
copia del mismo destinado a la realización de pruebas, esto
acelera mucho los procesos de testeo, siempre es conveniente tener
armada una estructura secundaria donde se puedan ingresar datos
con ánimo de probar y que estos no se vean involucrados con
los verdaderos.
Es muy útil también respetar siempre los números
de versiones así siempre se sabrá que el software
en ejecución es la última revisión del mismo.
Espero que este documento pueda ayudarlos a enfocar
de otra manera las pruebas de sus programas, no obstante recomiendo
que adquieran algo de bibliografía respecto al tema.
Y recuerden, hacer un sistema magistral cuesta mucho esfuerzo, probarlo
cuesta mas.
Bibliografía :
The art of software testing by Glenford J. Myers
Software engineering: a practitioner's approach by Roger S. Pressman
Software engineering with student project guidance by Barbee Teasley
Mynatt
|