Universidad Simón Bolívar
Dpto. de Computación y T.I.
Sistemas de Programas
Pruebas de sistemas orientados a objetos
Introducción
En general, el software es un artefacto complejo, voluminoso y relativamente
frágil, en el sentido que puede bastar un defecto para que falle
de manera catastrófica.
La elaboración de un software es un proceso propenso a errores:
los desarrolladores pueden cometer errores en la elicitación de
requerimientos, en la elaboración de los artefactos del análisis
y el diseño, en la codificación y hasta en el proceso mismo
de prueba.
Por ello, es importante revisar la consistencia intra- e inter-artefactos.
El sueño dorado de cualquier empresa que depende de software es
una herramienta que tome por entrada los requerimientos y produzca como
salida el software apropiado, haciendo así obsoleto al desarrollador
de software; el el desarrollador de software sueña en una herramienta
que automatice los pasos más mecánicos para pasar de un artefacto
a otro y que verifique automáticamente la consistencia entre esos
artefactos.
Cuando se valida o verifica la consistencia de un artefacto no ejecutable
respecto a otro artefacto no ejecutable, se habla de un proceso de inspección;
cuando
se valida un artefacto ejecutable (típicamente el código
del software) se habla de un proceso de prueba (testing).
Hay muchos tipos de pruebas. Entre ellas podemos mencionar:
-
Probar o validar el software contra los requerimientos funcionales (prueba
de requerimientos funcionales del sistema),
-
Probar el software a ver si cumple con los atributos establecidos para
él (prueba de desempeño, prueba de usabilidad, prueba
de instalabilidad, prueba de disponibilidad, prueba de stress, prueba de
mantenibilidad)
-
Probar unidades del software --rutinas o clases-- contra sus especificaciones
(pruebas unitarias de métodos o de clases).
Por razones de tiempo, en este curso nos limitaremos a probar el software
contra su Modelo de Uso. Este tipo de pruebas pueden considerarse
como un tipo de prueba de requerimientos funcionales del
sistema.
Cuán exhaustivamente queremos realizar las pruebas depende del
nivel de calidad a que aspiremos, el tiempo disponible para las pruebas
y el riesgo que estemos dispuestos a aceptar al ejecutar el software. Evidentemente
un software que desarrollo para divertirme no necesita someterse al mismo
proceso de pruebas que un software médico que se usará en
150 países y cuyas fallas pueden ser, literalmente, fatales. En
este curso nos limitaremos a realizar pruebas sencillas, poco exigentes,
apropiadas al caracter de prototipo del desarrollo; en la asignatura Ingeniería
de Software 3 (CI-4713) se presenta un proceso de prueba más exigente.
En todo proceso de prueba podemos distinguir al menos cuatro etapas:
-
Planificación del proceso de prueba;
-
Elaboración de casos de prueba;
-
Ejecución de los casos de prueba;
-
Evaluación de los resultados de las pruebas.
Planificación del proceso de prueba
La planificación del proceso de prueba incluye:
-
El nombre del coordinador del proceso de prueba;
-
El alcance del proceso de pruebas;
-
Las características del ambiente de prueba;
-
Los nombres de los responsables de las actividades de prueba;
-
El calendario y esfuerzo previsto (schedule) para las actividades.
En el alcance del proceso de pruebas se indica la configuración
del software que se somete a pruebas, los tipos de pruebas que se
llevarán a cabo, el grado de exigencia para cada tipo de prueba
y el criterio de terminación para las pruebas. El criterio de terminación
explicita las condiciones que debe satisfacer un proceso de pruebas para
considerarse satisfactorio. Algunos criterios posibles son:
-
Cuando el software ejecute todos los casos de pruebas sin producir fallas;
-
Cuando el software ejecute todos los casos de pruebas produciendo a lo
sumo fallas menores;
-
Cuando se agote el tiempo previsto para el software (!);
-
Cuando todas las clases críticas del software ejecuten sus pruebas
unitarias sin fallas, el sistema ejecute el 90% de los casos de pruebas
de requerimientos funcionales sin producir fallas y 60% de los casos de
pruebas de rendimiento.
Es importante saber, por ejemplo que un software debe probarse bajo Windows
98 pero no bajo Windows ME. Por ello, la sección
correspondiente a las características del ambiente de prueba permite
registrar las consideraciones especiales que se deben hacer para ejecutar
correctamente los casos de prueba (por ejemplo, revisar que se encuentre
la versión 1.5 del archivo a.txt en el directorio
~teruel/pruebas), así como dejar constancia de las versiones
de los software de apoyo (Sistema de Operación, compiladores, generadores
GUI etc.) y los modelos de máquinas que deben usarse en las pruebas.
Esto permite reducir las tareas de depuración debidos a defectos
de configuración.
Elaboración de casos de prueba
Un caso de prueba elaborado incluye:
-
Un nombre que identifica el caso;
-
Sus entradas: es la secuencia exacta de acciones que debe realizar el usuario
para ejecutar ejecutar el caso de prueba. Note que si las acciones involucran
introducir valores, deben especificarse los valores exactos que deben
introducirse;
-
Su salida esperada: Los resultados exactos observables que debe producir
el sistema al ejecutar el caso de prueba.
Idealmente debemos elaborar, en el menor tiempo posible, un conjunto
mínimo de casos de prueba que garantice el nivel de calidad deseado
para el software. En la realidad, debemos conformarnos con elaborar, en
un tiempo razonable, un conjunto reducido de casos de prueba, tal
que si el proceso de prueba cumple con el criterio de terminación,
la probabilidad de satisfacer el nivel de calidad deseado es suficientemente
alto.
Frecuentemente los casos de prueba se generan:
-
Improvisadamente. El tester se sienta con el software y se pone
a inventar, en caliente, casos de prueba. El mayor problema de este
enfoque es que, en general, no se garantiza la efectividad del proceso
de prueba en cuanto a que se detecten el número de defectos apropiados
al nivel de calidad a que se aspira.
-
Al azar. El tester genera los valores a introducir de manera aleatoria.
El mayor problema de este enfoque es que se necesita un número muy
alto de casos de pruebas para poder garantizar incluso niveles de calidad
muy bajos. La prueba estadística de software tiene sus adeptos,
pero el tema escapa los alcances de este curso.
La mayor parte de los defectos que se introducen en el software no son
defectos aleatorios. Podemos distinguir algunos modelos de defectos
que buscan caracterizar el tipo y número de defectos que se inyectan
en el desarrollo del software.
Por ejemplo, un modelo de defectos muy aceptado es el que postula que
una proporción importante de defectos son defectos en el manejo
de fronteras. Típicamente la frontera se fija incorrectamente,
por lo que valores que deben caer dentro de una frontera, caen fuera de
ella o vice versa. Así, para poner un caso sencillo, si un valor
de entrada debe ser un valor entero entre 1 y 10, el software valida incorrectamente
el valor introducido, permitiendo valores entre 0 y 10, 1 y 11 o 0 y 11
(es muy frecuente dentro de los defectos de frontera, la equivocación
por una unidad, defecto denominado en Inglés como off-by-one).
Por ende, si aceptamos el modelo de defectos en el manejo de fronteras,
es conveniente incluir casos de prueba que, valga la redundancia, pongan
a prueba las fronteras, es decir que sospechen que el desarrollador
cometió un error de fronteras en algún lado de su software.
El tester analiza pues los artefactos del desarrollador, buscando
pistas
que le sugieren la aplicabilidad de modelos de defectos. Así si
el tester encuentra en un contrato de evento de sistema, una precondición
a validar que indica que a>3, utilizará tal expresión
como pista para sospechar que el software valida incorrectamente la frontera
por lo que elabora dos casos de prueba, uno donde introduce el valor a=3
y
otro donde introduce a=4. Es importante hacer notar que estos
dos casos de uso están incompletos, dado que aún no he explicitado
la salida esperada del caso (a veces denominaremos
especificación
de un caso de prueba, a un caso incompleto o en el que se caracteriza
a una entrada sin precisarle un valor específico --p. ej. x>5).
En particular las pistas para el modelo de defectos de fronteras
pueden encontrarse en muchos artefactos del desarrollo:
-
Los requerimientos,
-
Los casos de uso,
-
Las restricciones de multiplicidad y las restricciones explícitas
de integridad en un diagrama conceptual (o en un diagrama de clases y/o
entidades),
-
Las precondiciones supuestas y por validar, las postcondiciones de un contrato;
-
Las descripción de la reacción del sistema ante un evento
del sistema;
-
Los elementos de flujo de control en un diagrama de colaboración;
-
El propio código.
En este curso prestaremos particular atención al modelo de defectos
de fronteras.
Ejecución de los casos de prueba
Para cumplir apropiadamente con esta etapa, llevaremos una bitácora
de pruebas donde se anotará:
-
El nombre del caso de prueba ejecutado;
-
La fecha y hora de la ejecución;
-
La salida obtenida;
-
El resultado de comparar la salida obtenida con la salida esperada: ok
(la salida obtenida está dentro del rango previsto por la salida
esperada), falla menor (la diferencia entre las salidas es una cuestión
de formato), falla grave, falla catastrófica (el sistema no completó
su salida o la completó pero se "cayó" despues).
Evaluación de los resultados de las pruebas
En esta etapa queremos aplicar el criterio de terminación del proceso
de pruebas para saber si podemos darlo por concluido exitosamente. En caso
negativo, debemos planificar el proceso de depuración y nuevas pruebas
(quién se encarga de qué, para cuándo). Al terminar
la depuración podemos:
-
Volver a aplicar (algunos de) los casos de prueba. Los casos que se vuelven
a ejecutar se denominan pruebas de regresión. En general
las pruebas de regresión incluyen no sólo aquellos casos
de prueba que no se ejecutaron satisfactoriamente, sino casos que se ejecutaron
satisfactoriamente. La razón de ello es confirmar que la corrección
de un defecto no introdujo un nuevo defecto (hay estadísticas que
sugieren que 1 de cada 5 defectos se introducen en el proceso de "depuración").
-
Generar nuevos casos de pruebas. Esto se hace por dos razones:
-
Evitar que el software se "adapte" a los casos y termine, en caso extremo,
ejecutando correctamente sólo los casos de prueba;
-
Probar más exhaustivamente aquellos aspectos del software que parecen
particularmente propensos a defectos. De nuevo, las estadísticas
parecen confirmar la llamada "ley de Zipf", en cuanto que un 20% del software
contiene el 80% de los defectos.
Para complicar las cosas, hay que tomar en cuenta que, a veces, las discrepancias
entre la salida esperada y la la obtenido se debe a ¡defectos en
los casos de prueba!
Finalmente es conveniente escribir un reporte final para la gerencia,
de modo que queden estadísticas sobre el proceso de prueba para
utilizarse en análisis post-hoc (a veces denominados post-mortem)
del proyecto de desarrollo. Típicamente la gerencia quiere saber
en forma precisa y resumida:
-
¿Cuál es el estado actual del software, en cuanto al nivel
probable de calidad que alcanzó?
-
¿Cuánto tiempo y esfuerzo se llevaron los procesos de prueba
y depuración?
-
¿Cuán efectivo fue el proceso de prueba? (¿cuántos
fallas/defectos se detectaron?)
-
¿Qué tipo de defectos se producen?
-
Recomendaciones concretas para el futuro (en cuanto al producto,
el proceso de desarrollo y de prueba). Si se recogen estadísticas
de los errores cometidos por etapa de desarrollo, dichas estadísticas
pueden servir de apoyo para proponer mejoras como la adquisición
de una herramienta CASE, mayor entrenamiento en un lenguaje de programación,
cambio de un lenguaje de programación, entrenamiento en técnicas
de diseño o reestructuración de grupos de trabajo.
Casos de uso como punto de partida para elaborar casos de prueba
Para concretar algunas de las ideas mencionadas previamente, en este curso
elaboraremos casos de prueba que permitan validar si el software
desarrollado cumple con las funciones descritas en sus casos de uso. El
nivel de exigencia que manejaremos para el proceso de proceso es relativamente
débil, cónsono con el desarrollo de prototipos o pilotos
de sistemas poco críticos.
Para elaborar los casos de prueba, y dependiendo del grado de exigencia
del proceso de prueba, haremos uso de los
-
Los casos de uso;
-
Los contratos (en menor grado).
Utilizaremos, como es costumbre en este curso, el ejemplo del sistema
de punto de venta del texto de Larman para ilustrar los conceptos.
Los casos de uso pueden escribirse de modo que cada caso de uso documenta
o especifica un uso posible del sistema o pueden corresponder a
sesiones
de uso del sistema.
Comenzaremos suponiendo que tenemos acceso a cada uno de los casos de
uso que describen un uso del sistema. Queremos por ende, revisar
si el sistema se comporta según lo especificado en ese caso de uso.
Escenarios de uso
Cada caso de uso describe varios escenarios de uso, entre
los que se distinguen:
-
Los escenarios correspondientes al curso normal del caso de uso;
-
Los escenarios que incluyen al menos un curso opcional del caso de uso;
-
Los escenarios que producen al menos una excepción.
Los escenarios correspondiente al curso normal del caso de uso son aquellos
que, como su nombre lo indican, siguen al pie de la letra el curso normal
del caso de uso. Para probar uno de estos escenarios debemos introducir
eventos al sistema parametrizados de tal forma de no salirse del curso
normal ni de producir excepciones que interrumpan ese curso. Note que los
cursos normales pueden incluir elementos de control de flujo como acciones
condicionadas o iteradas, como por ejemplo hacer un pago en efectivo, cheque
o con tarjeta de crédito (acciones condicionales) o comprar varios
tipos diferentes de productos (acciones iteradas). Para poder probar un
curso normal tenemos entonces que indicar no sólo los eventos del
sistema a introducir y el valor de sus parámetros, sino también
si se lleva a cabo o no cada acción condicionada y el número
de veces que se llevará a cabo las acciones iteradas.
Con la notación de casos de uso utilizada en el curso, puede
resultar dificil identificar los escenarios que producen al menos una excepción.
Para ello, puede ser necario revisar los contratos asociados a los eventos
del sistema mencionados en los casos de uso.
Para elaborar los casos de prueba debemos decidir cuáles de los
escenarios de uso queremos ejercitar.
Grados de exigencia
Debemos decidir el grado de exigencia con que queremos probar un caso de
prueba. En este curso consideraremos dos niveles:
Diremos que un software ha sido débilmente sometido a pruebas
contra un caso de uso ssi se ejecutan casos de prueba que:
-
Ejecutan al menos una vez cada acción condicionada de un curso normal;
-
Ejecutan cero( si aplica), una y más de una vez cada acción
iterable de un curso normal;
-
Ejecutan al menos una vez cada opción del caso de uso;
-
Producen al menos una vez cada posible excepción.
Ejemplo de un grado de exigencia débil
Elaboraremos un conjunto de especificaciones de casos de prueba para
probar débilmente al caso de uso Comprar Productos para el
primer incremento del sistema de punto de venta.
Recuerde que en el primer incremento, no se actualiza
el inventario de productos, no se puede cancelar una venta en progreso,
sólo se puede pagar en efectivo, y se supone que el Cajero siempre
tiene vuelto para el cliente.
Note que el escenario normal de este caso de uso contiene:
-
Una acción iterable (que produce y trata el evento introducirProductos(cup,cant)),
-
Una acción condicionada ("Si un producto se repite, el Cajero puede
introducir la cantidad").
El caso de uso contiene una opción (introducir un producto cuyo
código no está registrado en el catálogo). Note que
llevar a cabo la opción produce la única excepción
prevista para el caso de uso y en los contratos. Por ende, nos basta
con los siguientes casos de prueba para lograr una prueba débil
del caso de uso:
-
Un caso de prueba que ejercite el escenario normal y produzca cero
iteraciones de la acción iterable;
-
Un caso de prueba que ejercite el escenario normal, produzca exactamente
una iteración de la acción iterable y no se ejecute la acción
condicionada;
-
Un caso de uso que ejercite el escenario normal, produzca exactamente una
iteración de la acción iterable y no se ejecute la acción
condicionada;
-
Un caso de prueba que ejercite el escenario normal y produzca dos
o más iteraciones de la acción iterable;
-
Un caso de prueba que ejercite la opción al curso normal.
Precisemos cada especificación de caso de prueba:
-
Un caso de prueba en el que no se compren productos (es decir se acciona
terminarVenta()
como primer evento del sistema. Una interesante discusión se suscita
por dos aspectos:
-
¿Puede darse este evento sin que el caso de prueba incluya un evento
introducirProducto()
previo?;
-
¿Puede darse este evento sin que se incluya un evento pagar()
posterior?
Estas preguntas ilustran algunos de los problemas que pueden surgir por
utilizar lenguaje informal para describir los casos de uso.
-
Un caso de prueba en el que se compre exactamente un producto. Las acciones
que deben introducirse en este caso de prueba son:
-
Un evento introducirProductos(cup, cant) donde cup corresponde
a un código catalogado y cant es exactamente uno (el
cajero no debe tener que introducir la cantidad de productos);
-
Un evento terminarVenta();
-
Un evento pagar(monto) donde el monto sea mayor o igual al total
a pagar por la compra del producto introducido;
-
Un caso de prueba en el que se compre más de un producto del mismo
tipo. Las acciones que deben introducirse en este caso de prueba son:
-
Un evento introducirProductos(cup, cant) donde cup corresponde
a un código catalogado y cant es mayor que uno;
-
Un evento terminarVenta();
-
Un evento pagar(monto) donde el monto sea mayor o igual al total
a pagar por la compra de los productos introducidos;
-
Un caso de prueba en el que se compren varios productos. Este escenario
lo satisface las siguientes acciones:
-
Un evento introducirProductos(cup, cant) donde cup corresponde
a un código catalogado y cant es mayor o igual que uno;
-
Un evento introducirProductos(cup', cant') donde cup' corresponda
a un código catalogado diferente a cup y cant' es
mayor o igual a uno;
-
Un evento terminarVenta();
-
Un evento pagar(monto) donde el monto sea mayor o igual al total
a pagar por la compra de los productos introducidos;
-
Un caso de prueba en el que se ejecuta el curso alterno correspondiente
a introducir un código no catalogado:
-
Un evento introducirProductos(cup, cant) donde cup
corresponde a un código no catalogado y cant es mayor o igual
que uno;
Se presenta una discusión interesante entorno a si el caso de uso
debe incluir algún otro evento:
-
terminarVenta(): podría argumentarse que no debe incluirse,
si el primer incremento se limita a lanzar una excepción del cual
no se recupera (¡poco elegante!)
-
pagar(): la inclusión de este evento depende de la discusión
anterior, si el caso no incluye un terminarVenta(), es más
dificil argumentar que debe incluir un pagar(), pero si incluye
un terminarVenta(), puede argumentarse a favor o en contra de obligar
a incluir un evento pagar().
Grado medio de exigencia
Diremos que un caso de uso ha sido medianamente sometido a prueba si:
-
Se prueba al menos una vez cada acción condicionada de un curso
normal;
-
Se ejecuta cero( si aplica), una y más de una vez cada acción
iterable de un curso normal;
-
Se prueba al menos una vez cada opción del caso de uso;
-
Se produce al menos una vez cada posible excepción.
-
Se introducen valores de prueba de fronteras para cada parámetro
de cada evento de sistema introducido.
La idea de la frontera de un parámetro proviene por analogía
de variables restringido a un intervalo como por ejemplo, una variable
entera x cuyos valores están restringidos al intervalo [1..10].
Para poner a prueba la frontera es conveniente hacer pruebas en los que:
-
x= 0, valor máximo que no llega a ser un valor válido;
-
x=1, valor mínimo válido;
-
x=10, valor máximo válido;
-
x=11, valor mínimo que excede los valores válidos.
Note que las variables tipo Número o int siempre tienen al
menos dos fronteras (el mínimo valor representable y el máximo
valor representable).
Las variables pueden tener más de dos fronteras. Por ejemplo
x pertenece_a [1..10, 20..30]
puede considerarse como una variable con cuatro fronteras (los valores
extremos válidos serían 1,10,20,30).
En general si las restricciones sobre una variable puede expresarse
como una conjunción de predicados:
P1(x) && P2(x) && P3(x)
consideraremos que cada predicado representa al menos una frontera. Por
ende, tendremos que tratar de generar pruebas donde se prueben valores
de frontera válidos, es decir que cumplan:
y valores vecinos a la frontera, es decir que no cumplan con los predicados.
Esto lo haremos tratando de falsificar un predicado a la vez (para evitar
explosiones combinatorias), por lo que necesitaremos intentar generar casos
donde:
-
~P1(x) && P2(x) && P3(x)
-
P1(x) && ~P2(x) && P3(x)
-
P1(x) && P2(x) && ~P3(x)
Si tenemos dos parámetros x,y independientes (es decir
que el valor de uno de ellos no depende del valor que se le asigne al otro),
entonces, para evitar la explosión combinatoria de valores, no exigeremos
combinar exhaustivamente los n1 valores de prueba de frontera de
x con los n2 valores posibles de frontera de y para generar
n1*n2
casos.
Los valores de los parámetros se combinan de la siguiente forma:
-
Casos que combinan sólo valores válidos. Buscamos max(n1,n2)
casos, de modo que cada valor válido de frontera de cada variable
aparezca al menos en un caso de prueba. La malicia puede darnos sugerencias
sobre la mejor forma de combinar los valores de las diferentes variables.
-
Casos que asignan un valor inválido. Es usual limitar a que sólo
una variable de un caso de prueba puede tener un valor inválido.
Por ende, necesitamos n1+n2 casos para poner a prueba los valores
inválidos de frontera de dos variables independientes con n1
y
n2
fronteras.
En general, si tenemos n variables independientes, cada una de las cuales
tiene ci fronteras, necesitamos maxi_in[1..n]{ci}
casos que combinen valores válidos de frontera y sumatoriai
in [1..n]{ci} casos para valores vecinos a fronteras.
La dependencia entre variables puede reducir el número de casos
de prueba y afectar las combinaciones permitidas de valores en los casos
de uso.
Por ejemplo, podemos recurrir a los siguientes casos para probar medianamente
al caso de uso Comprar Productos para el primer incremento del sistema
de punto de venta.
-
Casos de prueba en el que no se compren productos. Hay dos posibilidades
que conviene probar con casos de prueba diferentes:
-
Se acciona terminarVenta() como primer evento del sistema. Como
no recurre a eventos con parámetros, podemos usar el mismo caso
para probar el sistema débilmente o medianamente.
-
Se acciona el evento introducirProducto(cup, 0), donde cup corresponde
a un código catalogado, seguido por la acción terminarVenta().
-
Casos de prueba en los que se compra exactamente un producto. Todos estos
casos comienzan por realizar las siguientes acciones:
-
Un evento introducirProductos(cup, cant) donde cup corresponde
a un código catalogado y cant es exactamente uno (el cajero
no debe tener que introducir la cantidad de productos);
-
Un evento terminarVenta();
y se distinguen por el valor que le proporcionan al parámetro monto
del evento pagar:
-
Un evento pagar(monto) donde el monto sea el mayor valor posible
menor al total a pagar por la compra del producto introducido (la interfaz
del usuario puede impedir que se pueda introducir tal valor, pero hay que
revisar que efectivamente lo haga);
-
Un evento pagar(monto) donde el monto sea igual al total a pagar
por la compra del producto introducido;
-
Un evento pagar(monto), donde el monto sea igual al máximo
valor que se pueda introducir por la interfaz;
-
Casos de prueba en el que se compre más de un producto del mismo
tipo. Por ejemplo:
-
Caso 1
-
Un evento introducirProductos(cup, cant) donde cup corresponde
a un código catalogado y cant es exactamente igual a dos;
-
Un evento terminarVenta();
-
Un evento pagar(monto) donde el monto sea igual al total a pagar
por la compra de los productos introducidos;
-
Caso 2
-
Un evento introducirProductos(cup, cant) donde cup corresponde
a un código catalogado y cant es igual al mayor valor que
se pueda introducir;
-
Un evento terminarVenta();
-
Un evento pagar(monto) donde el monto sea igual al total a pagar
por la compra de los productos introducidos.
Note que en este caso monto y cant no son variables independientes (el
monto debe ser mayor o igual al producto cant*precioUnitario). Por ende
introducir el mayor valor posible en cant, puede obligar este caso a producir
una excepción, si ese producto no es representable. Por ende puede
argumentarse que cualquiera que sea el valor asignado a monto, este caso
no constituye una prueba real para ese valor (pues no se llega a aplicar).
-
Un caso de prueba en el que se compren varios productos. Este escenario
lo satisface las siguientes acciones:
-
Un evento introducirProductos(cup, cant) donde cup corresponde
a un código catalogado y cant es mayor o igual que uno;
-
Un evento introducirProductos(cup', cant') donde cup' corresponda
a un código catalogado diferente a cup y cant' es
mayor o igual a uno;
-
Un evento terminarVenta();
-
Un evento pagar(monto) donde el monto sea mayor o igual al total
a pagar por la compra de los productos introducidos;
Note que cant, cant' están en el rango [1..maxIntroducible]
y monto está en el rango [totalCalculado..maxIntroducible].
Podemos cumplir con una prueba mediana con
-
cant, cant' valen 1, monto es igual al totalCalculado.
-
Un caso de prueba en el que se ejecuta el curso alterno correspondiente
a introducir un código no catalogado:
-
Un evento introducirProductos(cup, cant) donde cup
corresponde a un código no catalogado y cant es mayor o igual
que uno;
Prueba de sesiones de uso
Probar sesiones de uso significa probar la robustez de las sesiones,
es decir, queremos diseñar casos de prueba para determinar que los
usos del sistema permitidos dentro de una sesión no interfieren
entre si y respetan restricciones de orden o precondiciones entre ellos.
Típicamente queremos revisar aspectos como:
-
No se puede modificar una estructura sin haberla creado previamente;
-
Se respectan los modos de operación del sistema, es decir
se habilitan/deshabilitan opciones, acciones o usos según las restricciones
de uso del sistema.
-
Modificar una estructura no modifica estructuras semánticamente
irrelevantes.
Ejemplo
En el desarrollo del sistema de punto de venta, los usos, para un
actor tipo Cajero, son:
-
Comprar productos;
-
Devolver productos;
-
Abrir terminal;
-
Cerrar terminal.
Estos usos se agrupan en una sesión de uso de terminal de punto
de venta. Al probar la sesión queremos probar características
como:
-
Un terminal debe abrirse antes de poderse usar para comprar o devolver
productos, o cerrarse;
-
Pueden haber cero o más usos de Comprar Producto o Devolver
Producto entre un uso de apertura y uno de cierre de un terminal.
-
Ninguno de los usos puede anidarse, es decir no podemos empezar la compra
(o devolución) de productos dentro de una sesión en que ya
esté activo una compra --o una devolución-- de productos.
-
Un uso de compra de productos es independiente de las compras o devoluciones
anteriores, en el sentido que en una compra de productos no se inmiscuyen
renglones, subtotales o totales de una venta anterior.
Por otro lado, tambien tenemos sesiones de configuración del
sistema, en las que interviene el actor Administrador y, eventualmente,
sesiones
de análisis de venta para el actor Analista de Ventas.
Observaciones y recomendaciones finales
-
El uso de malicia
Si nos apegamos a lo indicado en las subsecciones anteriores, el diseño
de los casos de prueba parece una actividad bastante mecánica. Es
conveniente advertir que las pruebas más fuertes rara vez se generan
tan mecánicamente y el papel de la experiencia y la malicia del
tester
juega
un rol crucial.
Por ello, le recomendamos fuertemente agregar a las pruebas mínimas
indicadas anteriormente algunos casos donde Ud. ejercite ¡toda su
suspicacia y malicia!
-
Es usual aplicar diferentes grados de exigencia a las pruebas de diferentes
casos de uso. De nuevo, las presiones de tiempo pueden obligar a decidir
qué unas partes o aspectos del software serán probados más
cuidadosamente que otras. Los criterios para seleccionar el grado de exigencia
para los distintos casos de uso pueden tomar en cuenta factores como el
perfil
del usuario, la frecuencia estimada para el uso y las consecuencias
potenciales de que el software falle al ejercitar el uso. Por ejemplo,
en un sistema real de punto de venta, probablemente probaremos más
exhaustivamente aquellos aspectos donde hay mayor potencial de pérdidas
financieras.
-
Tradicionalmente la literatura distingue entre las pruebas caja blanca
y
las pruebas caja negra. En las primeras (caja blanca o transparente),
es lícito tomar en cuenta la estructura interna del software para
generar los casos de prueba; en las pruebas caja negra (es decir, caja
opaca)
no se toma en cuenta la estructura interna , por lo que las pruebas se
elaboran a partir de las especificaciones o descripciones que tenemos del
software. Es conveniente destacar que en en este curso sólo hemos
ejemplificado pruebas caja negra.
-
Las pruebas deberían planificarse mucho antes de que empiecen. En
cursos posteriores, veremos las ventajas que tiene adelantar la planificación
del proceso de prueba.
-
En la realidad, el coordinador del proceso de pruebas enfrenta muchos
desafíos complejos, tanto técnicos como psicológicos.
Tiene que aprender a generar suficientes casos de prueba para realmente
poner a prueba el software desarrollado, tiene que estar muy consciente
de las limitaciones de tiempo y tiene que aprender a manejar situaciones
potencialmente muy delicadas.
Esta página fue creada el 19 de junio de 2001 por el Prof. Alejandro
Teruel.
Ultima actualización: 22 de junio 2001.