TIM - L03 - 2023 - Tipos de Testing

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 13

Laboratorio de Testing de SW Página 1

UNIVERSIDAD CATÓLICA DE SANTA MARÍA


PROGRAMA PROFESIONAL DE INGENIERÍA DE SISTEMAS

SESIÓN 03:

Tipos de Testing

I
OBJETIVOS

• Conocer la importancia de los elementos de las Pruebas de Software


• Apreciar la forma de pensar en los diferentes elementos del Software Testing

II
TEMAS A TRATAR

v Testeando una GUI


v Inyección de fallas
v Problemas dependientes del tiempo
v Inputs no funcionales
v Tipos de Testing: De caja Blanca, de caja Negra
v Tipos de Testing: Testing Unitario, Testing de Integración, Testing de Sistema
v Tipos de Testing: Otros tipos de Testing

III
MARCO TEORICO

1. Testeando una unidad de interfaz gráfica

Estuvimos hablando que todo software bajo pruebas tiene un dominio y un rango
correspondiente a los valores que puede recibir y a los valores que entrega como salida.

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 2

Por otro lado, si estamos por ejemplo probando entradas a un software importante como un
sistema operativo, deberemos tener en cuenta que a pesar que el dominio de cada función esté
definido, el usuario final seguramente no malicioso, ingresará entradas de todo tipo, osea el
software sistema operativo debería estar preparado para todo.

Pensemos en el dominio y rango de una Interfaz gráfica cualquiera (GUI). El dominio sería
los clics, ingresos por teclado, los eventos, los lectores, etc. El rango sería los estados de la
aplicación GUI. Ahora debe ser evidente que el tamaño del dominio es gigantesco, pues hay
muchísimas posibles combinaciones de entrada.
Si pensamos en una buena forma de testear un GUI tendremos que pensar en probar la
aplicación totalmente, como si un niño de 4 años jugara con ella, inyectando eventos al azar,
y hasta usando eventos que hicieron quebrar a la GUI de versiones anteriores. La interfaz
gráfica no puede depender de un usuario para presentar siempre entradas válidas. Por tanto
una aplicación de interfaz gráfica de usuario tiene que estar lista para básicamente cualquier
cosa.

Hablemos ahora de una implicación final de la interacción entre los dominios de prueba y las
relaciones de confianza entre las diferentes entidades. Digamos que tenemos algún tipo de
interfaz aquí y en ambos lados tenemos gente que confiamos, por ejemplo a la izquierda yo y
a la derecha mis compañeros de trabajo. La pregunta es ¿puedo confiar en mis compañeros?
Y ¿mis compañeros pueden confiar en mi para generar siempre entradas al usar las diferentes
APIs que caigan dentro del dominio de las entradas? Por supuesto la respuesta generalmente
es NO. De hecho, ni siquiera puedo confiar en mi mismo para generar siempre entradas que
estén dentro del dominio de las entradas aceptables para las APIs. Esto nos lleva a la idea de
la codificación de la defensa, es decir la comprobación de los errores, por si misma para
detectar inconsistencias internas. En general, probar software llamando a las APIs que
proporcionan, es bastante sencillo. Nos limitamos a hacer llamadas a la API y vemos los
resultados. Pero algo incómodo sobre el software real es que este no solo proporciona APIs
sino que también los utiliza. Lo que quiero decir es que el software bajo prueba seguramente
tendrá la misión de llamar a bibliotecas, obtener valores de retorno del sistema operativo, y
seguramente de máquinas virtuales como por ejemplo el python en tiempo de ejecución.
Tomemos por ejemplo la idea que el software bajo pruebas es algo así como un navegador
web. Una idea es probar el navegador usando la API que proporciona vale decir su interfaz
gráfica de usuario y no preocuparse por las pruebas con respecto a las APIs que utiliza. ¿qué
tipo de APIs el navegador usa? Por un lado hablamos de la red, habla con el disco duro a
través del sistema operativo, esta hablando con todo tipo de interfaces de bajo nivel. Y a veces
las APIs no actúan como esperaríamos. Como un ejemplo simple, tomemos el caso de que el
navegador web es el almacenamiento de las cookies en el disco duro de la computadora local.
La mayor parte del tiempo durante las pruebas, esperamos que el almacenamiento y la
recuperación de las cookies operen con normalidad. Pero que pasaría por ejemplo si el disco
duro está lleno cuando el navegador intenta guardar una? ¿el navegador se bloquearía? ¿se
rompería su estado interno de alguna forma que se convertiría en imposible de utilizar? O con
suerte esperaría hasta que haya más espacio libre en el disco para guardar cookies otra vez?
Por supuesto que nos gustaría que nuestro navegador hiciera lo correcto, lo que sea que esto
fuera. Lo cierto es que tenemos que probar esto realmente. Si solo esperamos que el software
haga lo correcto, entonces una de las reglas de oro de las pruebas es que no debemos nunca
esperar que el software haga algo que tenemos que comprobar esa realidad. El problema es
que tenemos este problema bastante incómodo donde no controlamos cómo responde el
sistema operativo a las llamadas que hacemos. No podemos fácilmente hacer por ejemplo que
el almacenamiento de una cookie falle. En vez de eso tendríamos que hacer algo como crear
una partición de disco completo, arreglos para que nuestro navegador almacene las cookies y
luego ver cómo responde. En este caso específico, la creación de toda una partición es difícil

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 3

pero podría hacerse, pero hay muchas casos en donde el software de nivel inferior tiene modos
de fallo que realmente no se pueden simular con facilidad, independientemente de lo duro que
trabajemos. Por ejemplo si tenemos una llamada al sistema operativo Unix, con el comando
“read”.

Figura 1: comando read en Unix

Los programas en Unix reales están emitiendo llamadas para leer constantemente a veces
cientos de veces por segundo. Anteriormente estábamos preocupados por el dominio de la
llamada al sistema read, es decir el conjunto de posibles argumentos válidos para la llamada
al sistema read. Ahora estamos mucho más preocupados pues no solo estamos probando la
llamada al sistema read, estamos probando un programa que se ejecuta sobre el sistema
operativo Unix y lo que es la respuesta del sistema operativo de nuevo al proceso que nos
preocupa. Vemos que devuelve el número de bytes leídos desde un archivo, pero hay un hecho
interesante que la lectura se le permite leer menos bytes de los que realmente pedimos. Así
que va a devolver un número entre 0 y count, pero no sabemos cuanto vale count. Otra cosa
que podemos hacer es fallar simplemente, es decir retornar -1 a la aplicación, pero resulta que
hay un montón de razones diferentes para este tipo de fallo.

Figura 2: Tipos de fallo del comando read en Unix

Hay por lo menos 9 condiciones de error que representan el -1 devuelto por el sistema
operativo. El punto es que la aplicación que llama a read, el sistema operativo puede devolver
cualquiera de estos valores que no son semánticamente equivalentes. La aplicación podría
tener que hacer cosas distintas dependiendo de cual de estos valores se obtiene. El punto sería

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 4

que es muy difícil que la gente pruebe el navegador web con tantos distintos valores de retorno
de read. Nos quedamos con el software cuyo comportamiento es probable que no entendamos,
y por tanto es software que no se ha probado muy bien.
Hay realmente algunos que han buscado una buena solución a este problema, pero no la han
encontrado. El hecho es que una gran cantidad de programas reales que se ejecutan en sistemas
operativos no están preparados para algunos de estos errores, desde los más extraños que
producen respuestas mucho más extrañas que los sistemas operativos pueden enviar, y en
consecuencia cuando estas cosas pasan, se produce el malfuncionamiento del software.

2. Inyección de fallas

Desde el punto de vista de las pruebas, a menudo se puede utilizar la técnica llamada inyección
de pruebas para este tipo de fallos para hacerle frente a este tipo de problemas. Supongamos
por un momento que estamos utilizando la biblioteca de python para crear un archivo.
Suponemos que la ruta del archivo que estamos tratando de abrir es /tmp/foo. El comando
sería File=open(“/tmp/foo”, ‘w’) donde w significa que estamos abriendo el archivo con
permisos de escritura. Así que ahora nuestro problema de la aplicación de python es esta
llamada, podríamos tener un tiempo duro para probar el caso en el que falla la llamada a open,
pues en la mayoría de maquinas existe una carpeta /tmp por lo que este llamado casi siempre
podría tener éxito. Lo que podemos hacer en vez de ello es llamar a una función diferente a
open que podría ser my_open con la misma interfaz que el llamado a la función open, además
que su interfaz es idéntica, también lo será su aplicación o cuerpo de la función. Así que la
mayoría del tiempo lo que my_open hará es llamar a open. Así que lo que tenemos es una
función stub que es equivalente a open. La diferencia aquí es que nosotros escribimos el
código de my_open y lo que podemos hacer es que el sistema falle dentro de nuestra rutina.
Esto es una técnica de pruebas bastante común llamada inyección de fallas. En la práctica
debemos ser bastante cuidadosos con la inyección de fallas. Una cosa que puede pasar es que
si hacemos fallar al my_open con demasiada frecuencia, por ejemplo en un 50% del tiempo,
entonces un programa que lo esté utilizando, probablemente nunca despegue y se inicie. Un
programa que abre muchos archivos es muy probable que nunca tenga éxito en la apertura de
todos ellos, por lo que no estaríamos llegando a ningún lugar. Así que una cosa que se podría
hacer en my_open es inicialmente tener éxito en unas 100 llamadas por ejemplo y luego
empezar a fallar por ejemplo en un 1% del tiempo. Esto es solo un ejemplo que en la práctica
tendríamos que experimentar para hallar el rango correcto de tasas de fracaso es bueno para
llegar a probar el software que se está bajo el testing.
(quiz)
Generalmente no necesitamos inyectar todos los posibles fallos, sino aquellos que esperamos
puedan producirse en la práctica y esperamos que nuestro sistema sea robusto con respecto a
ellos.
Para resumir, tenemos nuestro software bajo pruebas y proporciona algunas APIs. Cada
colección de APIs de funciones y la mayor parte del trabajo que tenemos durante la prueba va
a llamar a estas funciones con distintos valores y ver los resultados. Además el software bajo
pruebas va a utilizar las APIs proporcionadas por por ejemplo el sistema operativo o el runtime
del lenguaje de programación. Y un tipo separado de pruebas estará observando cómo el
software bajo pruebas responde al retorno de códigos y similares que le atribuyen las APIs que
utiliza.

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 5

3. Problemas dependientes del tiempo

Sería una cosa grandiosa si ambos tipos de pruebas representarían una visión completa de todo
lo que necesitamos para poner a prueba, pero en realidad no es el caso, y hay algunas
complicaciones añadidas. La cuestión es que las interfases explícitas abiertas que vemos aquí
no representan todas las entradas y salidas posibles sobre las cuales podamos cuidarnos, por
ejemplo en el lado de la entrada es completamente posible que nuestro software bajo prueba
tenga en cuenta el tiempo en que las entradas lleguen. Entonces puede darse el caso de que
nuestro software bajo prueba responda de manera diferente si dos entradas llegan muy juntas
de lo que lo haría si llegan 2 entradas separadas por un espacio razonable de tiempo.
Esto parece una tontería, pero basta con considerar por ejemplo, el software que procesa los
clics del ratón. Dos clics dados juntos representan un doble clic y esto es interpretado de
manera muy diferente de 2 clics del ratón que se producen más separadamente en el tiempo.

Figura 3: clics del mouse ingresados a un sistema

Otro ejemplo es algo así como un navegador web donde si los datos correspondientes a una
página web se devuelven en un corto periodo de tiempo, estos datos conseguidos representan
una página web, pero si los datos procedentes de la red se dispersar a través del tiempo estos
dan lugar a una especie de tiempo de espera, es decir el software bajo prueba que es nuestro
navegador web hará que una cierta clase entregue una página de error en lugar de realmente
representar los datos si se trata con demasiada lentitud. Estos dos ejemplos que acabamos de
ver son bastante fáciles de probar pues en cada caso tenemos la distinción binaria entre la
llegada rápida de datos, es decir un doble clic o una página web completa que llega antes de la
hora, y en el otro caso tenamos datos que llegan muy lentamente vale decir 2 clics separadas
o una página web que lleva tanto tiempo en llegar que el tiempo de espera acaba.
En otros casos la entrada dependiente de temporización puede hacer significativamente la vida
más complicada.

Tenemos otro ejemplo, de la máquina llamada Therac 25, que en los años 80 era una máquina
de terápias de radiación y era usada para dar altas concentraciones de haz de radiaciones a
ciertas partes del cuerpo humano donde se usaba para destruir tejidos cancerosos sin dañar el
tejido cercano. Como podrán notar esta no es una tecnología inherentemente segura. Va a
depender de las habilidades de los operadores además de un software altamente seguro para
ser usados en pacientes con cancer sin dañarlos. Lo que pasó con la Therac 25 fue una serie

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 6

de trágicos errores, donde 6 personas fueron sometidas a la sobredosis masiva de radiación y


varias de estas personas murieron a causa de lo mencionado. En realidad fue algo terrorífico
y atemorizante los accidentes sucedidos. Aquí hablaremos de una parte del software
perteneciente al Therac 25, que era un dispositivo en gran parte controlado por software y que
tenía en ese momento un controlador bastante sofisticado. Resultó que los programadores que
desarrollaron el software insertaron ciertos errores en su codificación. El bug específico que
mencionaremos aquí era llamado la condición de carrera (raise condition) y lo que era en
realidad se describe como un escenario donde los diferentes hilos de ejecución fallaron al estar
desincronizados, cuyo resultado fue que el software que contiene la condición de carrera podía
cometer errores. Entonces este software implicaba la entrada de datos desde el teclado de la
máquina de terapia de radiaciones del Therac 25, utilizada por los operadores de las terapias
al tratar a pacientes. Lo que sucedió fue que cuando el operador escribía lentamente era poco
probable que el bug se dispare. Y por supuesto mientras la máquina se estaba probando, las
personas no escribían muy rápido. Pero por desgracia, ya que los operadores de los hospitales
se hacían más y más familiares con el sistema al tratar a cientos de pacientes, estas personas
se volvieron diestros al usar esta máquina y comenzaron a tipear cada vez más rápido y con el
tiempo desencadenaron este error. Y el efecto de este bug era entregar sobredosis masiva de
radiación a los pacientes que finalmente produjo algunas fatalidades.
Entonces el tipo de escenario que este ejemplo presenta a los testers es que hay que preocuparse
seriamente por el tiempo en que las entradas llegan a nuestro software bajo prueba. Así
software como el del Therac 25 o para el Unix kernel a la hora a la que llegan las entradas es
muy relevante. Por otro lado, a menos que seamos extremadamente cuidadosos, la función de
raiz cuadrada que estuvimos hablando no se preocupa por el tiempo en que las entradas le
llegan.
Entonces, para averiguar si realmente los tiempos importan para el software bajo pruebas, hay
que pensar en sus especificaciones. Pensemos en que estamos tratando de hacer, en sus
necesidades y también en el código fuente del software. Busquemos en cosas como tiempos
de espera, temporizadores para dormir temporalmente algún proceso, y valores de cálculos que
dependen de la hora en que las cosas suceden. Si estos detalles se encuentran en el código,
entonces es muy posible que se deba tomar en cuenta el tiempo para la evaluación del software,
y esto significa por ejemplo, crear casos de test con la entrega de algún ingreso al software
bajo prueba de manera veloz, otros de manera rápida, otros a velocidad normal, otros a
velocidad más lenta y otros a muy lenta velocidad, para ver cómo es que el mismo responde.

4. Entradas no funcionales

Veremos aquí entonces es seguir profundizando frente a nuevas matices de pruebas de


software, mientras seguimos buscando en las cosas que pueden hacer pruebas de fuerza. Otra
cosa que ahora veremos que podría complicar las pruebas es lo que podríamos llamar entradas
o inputs no funcionales. Estas son entradas que afectan a la operación de un software bajo
prueba que realmente no tienen nada que ver con las APIs proporcionadas por el software que
estamos probando, ni tampoco tienen nada que ver con las APIs que usan en el software que
estamos probando. Es decir incluyen cosas como los cambios de contexto (context switches)
por ejemplo los switches entre diferentes hilos de ejecución en un software multihilos bajo
prueba. Ahora, no deberíamos preocuparnos en los cambios de contexto entre hilos si nuestro
software bajo prueba solo usa un simple hilo de ejecución. En general es muy común que los
sistemas de software complejos trabajar con múltiples hilos de ejecución. Así, la cosa es que
estos múltiples hilos de ejecución se han programado a lo largo de diferentes procesadores en
máquinas físicas donde se ejecutan en diferentes momentos. Y es el sistema operativo el que

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 7

toma las decisiones sobre qué hilo corre sobre qué procesador en qué momento. Y dependiendo
de la programación de estos hilos, las fallas en el software bajo prueba ya sea se pueden ocultar
o ser revelados y el problema es que el momento que suceden estos cambios de contexto no
está completamente bajo nuestro control o bajo el control de nuestra aplicación, es bajo el
control del sistema operativo que proporciona estas entradas no funcionales y esto hace que
las pruebas a software multihilos sea realmente muy difícil.
Aquí tenemos otro ejemplo de entradas no funcionales. En una empresa que desarrollaba un
hardware de red muy rápido, tal que dejaba 2 pcs hablando a velocidades de multigigabits
mediante un interruptor, y así algo interesante sobre el software que se estaba desarrollando
que corría no solo en un pc sino en una tarjeta de red, era que éste era completamente
independiente de la pila de TCP/IP. También se supone que debía proporcionar una entrega
confiable de datos incluso cuando existieran errores en la red. Así apareció un problema en
este sistema cuya red era muy fiable, se introdujeron errores de bit en los datos que se
transmitían una vez cada varias horas o incluso cada varios días, y así lo que nos enfrentábamos
era a una dificultad real en las pruebas del software de fiabilidad de extremo a extremo.
Entonces lo que se hizo era abrir un interruptor, la exposición de todos los contactos eléctricos
en el interior. Luego se tomó una llave de metal y se ejecutó a través de los contactos que
fueron expuestos de las fichas interiores del switch. Lo que esto podía hacer era introducir
una gran cantidad de pequeños cortocircuitos, causando un gran número de errores de bit,
logrando que el software tuviera que hacerle frente a todos estos errores de bits. Por supuesto,
ya sea interrumpiendo la red por un momento y reiniciando la transferencia de datos. Entonces
se generaba diferentes tipos de errores, ya sea de transmisión de bits y en cuyo caso se tenía
depuraciones que hacer. Lo que finalmente se pudo ver en este ejemplo es que se podría
acceder a través de este dispositivo de switch a ejecutar una clave a través de los contactos a
nivel profundo del sistema e introducir errores no se era capaz de probar de otra forma
conveniente en cualquier nivel de la pila de software, por lo que esta forma era un tipo de
entrada no funcional para el sistema bajo prueba.

5. Tipos de Testing: De caja Blanca, de caja Negra

Lo que a continuación veremos son los distintos tipos de Testing, sin ser exhaustivos ni
detallistas, va a ser un conjunto superpuesto de diferentes categorías.
El primer tipo de pruebas que hemos escuchado hablar son las pruebas de caja blanca (White
box testing). Estas se refieren a que el probador está utilizando un conocimiento detallado
sobre el funcionamiento interno del sistema, con el fin de construir mejores casos de pruebas
(test cases).
El complemento a las pruebas de caja blanca son las pruebas de caja negra (Black box testing).
Esto significa que no estamos usando conocimiento detallado del funcionamiento interno del
sistema, estamos en vez de probar el sistema en base a lo que sabemos sobre cómo se supone
que debe responder a los casos de prueba.

6. Tipos de Testing: Testing Unitario, Testing de Integración, Testing de Sistema

Testing de unidad, algo que seguramente veremos en mucho detalle más adelante en el curso.
Testing de unidad significa ver una pequeña unidad de software a la vez, y probarlo de una
manera aislada. Es mas o menos lo que estuvimos viendo con las pruebas al sistema de raíz
cuadrada, que anteriormente analizamos. La principal diferencia de este tipo de testing frente

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 8

a los otros es que estamos probando una pequeña cantidad de software. Con frecuencia es la
misma persona que la implementó o implementó el módulo, lo que significaría que estaríamos
haciendo también pruebas de caja blanca. Sin embargo las pruebas unitarias también pueden
realizarse según las pruebas de caja negra.
La meta en las pruebas de unidad es encontrar defectos en la lógica interna del software bajo
pruebas lo antes posible para así crear software lo más robusto posible para poder componer
luego con otros módulos y terminar así con un sistema de software que realmente funcione.
Además del tamaño del software bajo prueba, otra cosa que distingue a las pruebas unitarias
de otro tipo de pruebas es que generalmente no tenemos ninguna hipótesis sobre los patrones
de uso de software bajo prueba. En otras palabras, intentamos probar la unidad con entradas
de cualquier parte de su dominio. Existen muchas herramientas que soportan estas pruebas de
unidad. Python tiene una serie de marcos de pruebas unitarias, también cuenta con una serie
de marcos (frameworks) de lo que se llaman los objetos de imitación o falsos objetos que
podemos atornillar al software bajo pruebas para burlarse de la conducta del software más
grande en donde trabaja el software bajo prueba. Veremos algunos enlaces a algunas de estas
herramientas.
Lo siguiente que veremos es el tipo de software llamado de integración. Test de integración
significa a tomar múltiples módulos de software que ha sido probados con testing de unidad y
probarlos en combinación unos con otros. A este punto lo que realmente probamos son las
interfaces entre los módulos y la pregunta es si podemos definirlos al nivel necesario como
para que las diferentes personas que implementaron los diferentes módulos serán capaces de
hacer suposiciones compatibles que sean necesarias para que estos módulos puedan trabajar
juntos. Y a menudo sucede que durante las pruebas de integración encontramos diferencias
sustanciales de hipótesis que tienen que ser resueltas antes de que podamos crear una
aplicación integrada. Y así, un profesor de ciencias de la computación encuentra que sus
estudiantes salen exitosos siempre frente a la creación de módulos de software bien probados
y fiables, pero grupos separados de estudiantes individualmente crear módulos de software,
incluso cuando la aplicación de una especificación es bastante robusta, con frecuencia crean
módulos de software que fracasan en pruebas de integración. Es realmente algo complicado
de hacer. El pasar el testing de integración es algo más complicado que pasar el testing de
pequeñas unidades.
El siguiente tipo de testing es el testing de sistema. Aquí haremos una pregunta diferente a lo
que hacíamos al hacer testing de integración y de unidades. Aquí preguntamos si el sistema
como un todo logra cumplir sus metas. Y a menudo en este momento estamos haciendo
pruebas de caja negra, y esto por 2 razones. Primero porque el sistema es probablemente lo
suficientemente grande por ahora que un conocimiento detallado de sus componentes internos
puede no ser de mucha ayuda en la creación de buenos casos de prueba. La segunda razón es
que a esta altura no estamos interesados en saber y probar lo que está pasando en el interior
del sistema, comparado con saber si el sistema cumple o no sus metas establecidas, y por lo
tanto otra cosa que diferencia a las pruebas de sistema de las unitarias es el hecho de que en
este nivel a menudo nos interesa la forma en que será usado el sistema. Y el hecho es que a
este nivel no nos interesa que el sistema funcione para todos los posibles casos de uso, en vez
de eso nos interesa asegurarnos que simplemente funcione aceptablemente para los casos de
uso importantes.

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 9

Figura 4: Test de unidad, Test de integración y Test de Sistema

7. Tipos de Testing: Otros tipos de Testing

Otro tipo de pruebas ortogonales a las pruebas unitarias, pruebas de integración y pruebas de
sistema, es una técnica de pruebas llamadas pruebas diferenciales. En las pruebas diferenciales
lo que se hace es tomar la misma entrada de la prueba hacia 2 implementaciones diferentes del
software bajo prueba (Ver figura 5) y su comparación entre ellos hacia la igualdad. Estas
pruebas diferenciales son herramientas muy potentes en el caso de que tengamos varias
versiones.

Figura 5: Testing Diferencial

Otro tipo de testing es el llamado testing de estrés, el cual es un tipo de testing en el que se
pone a pruebas a un sistema más allá de sus límites normales de uso y es descrito mejor viendo
los siguientes ejemplos. Por ejemplo para la función raíz cuadrada podríamos probarlo con un
número muy grande o números muy pequeños. Para un sistema operativo podríamos probarlo
haciendo un gran número de llamadas al sistema o haciendo un gran número de ubicaciones
de memoria o creando archivos de gran tamaño. Para un servidor web podríamos probarlo
haciendo muchas peticiones o muchas peticiones a una base de datos que se comunique con
su base de datos física. Las pruebas de estrés se realizan normalmente para evaluar la solidez
y la fiabilidad del software bajo prueba.
Y por último, tenemos las pruebas al azar (Ver Figura 6). En random testing se utilizan los
resultados de un generador de números pseudo-aleatorios para crear aleatoriamente entradas

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 10

de pruebas y librar todos hacia el sistema bajo prueba. Y a veces solamente estamos enviando
valores sin sentido, datos aleatorios para el software bajo pruebas; pero a menudo esto puede
ser mucho más sutil y sofisticado que solo lanzarlos sobre el software bajo prueba.

Figura 6: Test de estrés y test aleatorio

Las pruebas al azar muy a menuno son útiles para encontrar casos extremos en los sistemas
de software y el programa crashme para Linux que hablamos antes, es un ejemplo de un
probador al azar.

IV
(La práctica tiene una duración de 4 horas) ACTIVIDADES

1. Entender y valorar cada concepto de esta práctica.


2. Buscar herramientas para Testing de Software en cada una de sus clasificaciones, vale decir
para testing de caja negra, caja blanca, caja gris, testing de unidad, testing de integración,
testing de sistema, testing diferencial, testing de estrés, testing de regresión y testing al
azar. Al menos uno por cada tipo.

V
EJERCICIOS

1. Describe la diferencia entre las estrategias de caja Blanca y la de caja negra.

2. Supón que un tester cree que una unidad contiene un defecto de especificación. ¿Cuál estrategia
de testing sería la mejor para descubrir el defecto y por qué?

3. Explica las diferencias entre testing aleatorio y testing usando una suposición errada.

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 11

4. Define las clases de equivalencia y desarrolla un conjunto de test cases para cubrirlos para la
siguiente descripción del módulo. El módulo acepta un color para una parte. La elección del color
son {RED, BLUE, YELLOW, VIOLET}.

5. Define las clases de equivalencia y valores límite y desarrolla un conjunto de test cases para
cubrirlos para la siguiente descripción del módulo: El modulo es parte de un sistema de membresía
de TV pública. El modulo permite la entrada de una contribución de $0.01 hasta $99,999.99.
Además ingresa un estado de membresía para el contribuyente que puede ser: regular,
estudiante/retirado, o club de estudio.

6. Desarrolla casos de test de tipo caja negra usando análisis de valores de partición y límites de
clase de equivalencia, para probar un modulo que es componente de software de un sistema
automático de respuesta oral. El modulo lee el monto que el usuario desea retirar de su cuenta. El
monto debe ser un múltiplo de $5.00 y menor o igual que $200.00. Asegurar listar cualquier
suposición que se haga y etiquetar las clases de equivalencia y valores límite que usas.

7. Dibuja un diagrama de transición de estado para una máquina de pila simple. Asume que la pila
mantiene n ítems donde n es un número positivo pequeño. Tiene las operaciones de ‘‘push’’ y
‘‘pop’’ que causan que el puntero a la pila incremente o decremente, respectivamente. La pila
puede ingresar estados tales como ‘‘full’’ si contiene n ítems, ‘‘empty’’ si no contiene ningún
ítem. Pop o sacar un ítem de una pila vacía, o push o meter un ítem a una pila llena causará una
transición a un estado de error. Basado en un diagrama de transiciones de estado, desarrolla un
conjunto de casos de test de caja negra que cubra las transiciones de estado claves. Asegúrate de
describir la secuencia exacta de entradas, así como la secuencia esperada de cambios de estado y
acciones.

8. La siguiente es una especificación para una unidad de software:

La unidad calcula el promedio de 25 números de punto flotante que caen entre valores límites los
cuales son valores positivos entre 1.0 (valor mínimo permitido) hasta 5000.0 (valor máximo
permitido). Los valores límite y los números son ingresados a la unidad. El límite superior debe
ser mayor que el límite inferior. Si un conjunto inválido de valores es ingresado para los límites,
un mensaje de error aparecerá y se solicitará al usuario reingresar los datos. Si los valores límite
son válidos, la unidad calcula la suma y el promedio de los números que están en dicho rango,
incluido los límites. El promedio y la suma es la salida de la unidad, así como el número total de
entradas que caen en los límites.
Derivar un conjunto de clases de equivalencia para la unidad de promedios usando la
especificación, y complementa las clases usando un análisis de valores de límites. Asegúrate de
identificar las clases válidas e inválidas. Diseña un conjunto de casos de test para la unidad usando
sus clases de equivalencia y valores límite. Para cada caso de test, especifica las clases de
equivalencia cubiertas, los valores de entrada, las salidas esperadas y los identificadores de casos
de test. Muestra en forma tabular que has cubierto todas las clases y límites. Implementa este
módulo en un lenguaje de programación de tu elección. Ejecuta el módulo con tus casos de test y
graba las salidas actuales. Graba una versión incorrecta del programa para uso futuro. Provee un
reporte de defectos que contenga todos los defectos que encuentres usando los casos de test. Los
defectos deben tener un número de identificador y una descripción. Clasifica los defectos en las
siguientes categorías: Defectos de requerimientos y especificaciones, defectos de diseño, defectos
de código, y defectos de testing. Comenta sobre la efectividad de los casos de test al encontrar los
diferentes tipos de defectos.

9. Para la especificación del Problema 8, identifica las condiciones de entrada y salida y las reglas
que las relacionan. Tabula tus resultados y dibuja un grafo causa-efecto. Desarrolla una tabla de

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 12

decisión y desde esa tabla crea un conjunto de casos de test. Ejecuta tu módulo original
desarrollado en el problema 8 con estos casos de test y compara los resultados con aquellos
obtenidos desde la partición de clases de equivalencia. Comenta tus observaciones.

10. Supongamos que un programa permite a un usuario buscar el nombre de la pieza en un grupo
específico de registros de partes. El usuario ingresa el número de registro que se cree que contiene
la parte y el nombre de la parte que se busca. El programa informará al usuario si el número de
registro está dentro del rango legal de números de registro permitidos (1-1000). Si no es así, se
emitirá un mensaje de error: "el número de registro está fuera de rango". Si el número de registro
está dentro del rango y la pieza se encuentra, el programa devolverá "parte encontrada". de lo
contrario, informará "parte no encontrada". Identifique las condiciones de entrada y salida, es
decir, causas y efectos. Dibuje un gráfico de "causa y efecto" y una tabla de decisiones para esta
especificación. Desde la tabla genere un conjunto de entradas de prueba y salidas esperadas.

11. Describe las circunstancias bajo las cuales aplicarías las técnicas de caja negra y las de caja
blanca para evaluar un componente COTS.

12. Supón que estas desarrollando un ensamblador simple cuya sintaxis está descrita a continuación:
<statement :: label field op code> <address <label field> :: ‘‘none’’ | <identifier> :
<op code> :: MOVE | JUMP <address> :: <identifier> | <unsigned integer>

Una cadena de ttokens es la entrada al ensamblador. Los estados posibles para tal ensamblador son:
S1, prelabel; S2, label; S3, valid op code; S4, valid address; S5, valid numeric address. Start, Error, y Done.
La siguiente es una tabla que describe las entradas y acciones para el ensamblador:

Inputs Actions
No más tokens A1: Coloca la etiqueta en la tabla de símbolos.
identificador A2: Busca el código de op y almacena su valor binario en el campo op code.
MOVE, JUMP A3: Busca el símbolo en la tabla de símbolos y almacena su valor en el campo address.
Coma A4: Convierte el numero en binario y almacena el valor en el campo address.
Integer A5: Coloca la instrucción en el módulo objeto e imprime una línea en el listado.
A6: Imprime un mensaje de error y coloca puros Ceros en la instrucción.
Usando esta información y otras que necesites asumir, desarrolla un diagrama de transición de estados para el
ensamblador. Usando el diagrama de transición de estados, desarrolla un conjunto de casos de test que cubrirán
todos los estados de transición. Asegúrate de describir la secuencia exacta de entradas así como la secuencia
esperada de cambios de estados y acciones.

13. Describe el rol que juegan los managers al institucionalizar las técnicas y métodos de testing básicos, tales como
aquellas descritas aquí.

VI
CUESTIONARIO

I. Defina los siguientes términos:


1. APIs
2. GUIs
3. cookies
4. Therac 25
5. crashme software

II. Defina rápidamente los tipos de testing:

M. Sc. Carlo Corrales D. Sesión 03


Laboratorio de Testing de SW Página 13

1. testing de caja negra


2. testing de caja blanca
3. testing de caja gris
4. testing de unidad
5. testing de integración
6. testing de sistema
7. testing diferencial
8. testing de estrés
9. testing de regresión
10. testing al azar

VII
BIBLIOGRAFIA Y REFERENCIAS

Ø Ilene Burnstein. Practical Software Testing. Springer Prof. Computing. New York Inc.
2003.
Ø Myers, Glenford J., The art of software testing, New York Wiley, 1979. ISBN: 0471043281.
Ø Hetzel, William C., The Complete Guide to Software Testing, 2nd ed. Wellesley, Mass. :
QED Information Sciences, 1988. ISBN: 0894352423.
Ø Savenkov, Roman (2008). How to Become a Software Tester. Roman Savenkov Consulting.
p. 159. ISBN 978-0-615-23372-7.
Ø Ron Patton. Software Testing (2nd Edition). 2005.

M. Sc. Carlo Corrales D. Sesión 03

También podría gustarte