You are on page 1of 11

Técnicas para diseñar casos de prueba del software

Carlos Núñez Lay


RESUMEN
Dada la actual ubicuidad del software, un mal funcionamiento del mismo puede
ocasionar desde la molestia por un mensaje inapropiado, hasta la pérdida de
cuantiosas sumas de dinero o, peor aún, de vidas humanas. Por ello, el
software, a semejanza de cualquier artefacto tangible, debe ser sometido a
pruebas para evaluar si cumple adecuadamente con lo que se espera de él.
Aunque el desarrollador de software realiza pruebas del mismo, estas –aún en
empresas con personal dedicado a esta actividad específica- se efectúan de
manera intuitiva e informal. Dada la creciente demanda de software de calidad,
la prueba o “testeo” del software en una actividad que ha evolucionado con el
uso de diversas técnicas y herramientas que la alejan del arte y la acercan a la
ingeniería.
En las siguientes secciones se presentan los conceptos fundamentales de la
prueba del software y se revisan algunas técnicas para diseñar casos de
pruebas. Dentro del conjunto de estas técnicas se han escogido las referidas a
las pruebas unitarias del código ejecutable, tanto desde un enfoque funcional
(“caja negra”), como desde un enfoque estructural (“caja transparente” o “caja
blanca”). Aunque también son importantes para la calidad del software, no son
motivo de este artículo las pruebas estáticas, tales como las inspecciones
grupales del código, o la revisión de programas por los colegas.

1. INTRODUCCIÓN
Según la IEEE, probar el software es analizarlo para detectar diferencias entre
las condiciones existentes y las requeridas, y para evaluar las características
del mismo.
Obsérvese que -según esta definición- probar el software no consiste en
mostrar que el software realiza las funciones requeridas (“que funcione”). Por el
contrario, se debe partir de la suposición de que el software contiene defectos y
de que la prueba se realiza para detectar la mayor cantidad posible de estos y
– en consecuencia – aumentar su confiabilidad y su calidad. El trabajo de
probar software consiste –entonces- en diseñar casos de prueba, llevarlos a
cabo en un entorno controlado, detectar defectos, documentarlos y reportarlos.
Dado que usualmente se tienen limitaciones de tiempo y recursos, el número
de casos de prueba debe ser finito y, por tanto, lo más eficientes posibles, es
decir, que tenga una alta probabilidad de encontrar defectos. Probar todos los
casos posibles puede resultar imposible, mientras que hacerlo con casos
elegidos aleatoriamente encierra una alta probabilidad de no detectar defectos.
En este artículo se describen algunas técnicas para diseñar casos de prueba
del código ejecutable a nivel unitario. En la secciones siguientes se enumeran
los principios que deben guiar este tipo de pruebas y las dos clases de técnicas
que nos permiten diseñar casos de prueba eficientes: de caja negra o
funcionales y de caja transparente (o blanca) o estructurales.
2. PRINCIPIOS DE LA PRUEBA DEL SOFTWARE
Estos principios son importantes porque guiarán el accionar del profesional que
prueba del software. Ilene Burstein señala los siguientes, reformulando los
establecidos originalmente por Glenford J. Myers:
Principio 1
Probar es el proceso que consiste en ejecutar un componente de
software utilizando un conjunto de casos de prueba previamente
seleccionados con la intención de detectar defectos y de evaluar su
calidad.
Esto supone separar las pruebas de la depuración o “debugging”,
actividad esta que se refiere a reparar el software eliminando los
defectos.
Principio 2
Un buen caso de prueba es aquel que tiene una alta probabilidad de
hallar defectos aún no detectados.
Partiendo de la hipótesis de la presencia de un determinado tipo de
defecto, se escogen las condiciones de entrada, se determinan los
resultados esperados y se realiza la prueba para determinar si el defecto
está o no presente.
Principio 3
Los resultados de cada prueba deben ser revisados meticulosamente.
Si un defecto es pasado por alto o si se declara –equivocadamente- la
existencia de un defecto que no es tal, las consecuencias pueden ser
muy costosas.
Principio 4
Cada caso de prueba debe incluir el resultado esperado.
El resultado esperado es lo que permitirá determinar si existen o no
defectos.
Principio 5
Los casos de prueba deben incluir condiciones de entrada válidas e
inválidas.
La robustez del software se puede evaluar probando su funcionamiento
con entradas inválidas.
Principio 6
La probabilidad de que existan defectos adicionales en un componente
de software es directamente proporcional al número de defectos ya
detectados en ese componente.
Este principio se basa en la evidencia empírica. Las razones pueden ser
el nivel de complejidad o algunos defectos de diseño.
Principio 7
Las pruebas deben ser conducidas por personas independientes a las
que hicieron el desarrollo.
El desarrollador está síquicamente preparado para que su obra funcione
“bien”, de modo que le será muy difícil asumir el principio 1: detectar
defectos.
Principio 8
Las pruebas deben ser repetibles y reutilizables.
Las pruebas deben ser repetidas luego de haberse reparado el defecto.
Además también serán muy útiles para las pruebas de regresión, es
decir, las que se realizarán cuando, por razones de evolución o mejora,
el software tenga que ser modificado.
3. TÉCNICAS DE CAJA NEGRA
El término “Caja Negra” se refiere a que con este enfoque se deja de lado la
estructura interna del software, es decir, solo se toma en cuenta qué hace (o
debe hacer) el software, mas no el cómo lo hace. La materia prima para estas
técnicas puede ser la descripción funcional del software, las condiciones de
entrada y de salida, o un diagrama Entrada-Proceso-Salida bien especificado.
Las técnicas de Caja Negra permiten revelar defectos y/o ambigüedades en los
requerimientos y en las especificaciones del software.
3.1. Particiones de Equivalencia
En esta técnica, el dominio de datos de entrada es particionado en una
cantidad finita de clases de equivalencia (válidas e inválidas). Cualquier dato
miembro de una clase de equivalencia será un elemento representativo de
dicha clase y permitirá obtener información (revelar defectos) acerca del
comportamiento del software con los datos de esa clase de equivalencia.
Realizar pruebas para todas y cada una de estas clases de equivalencia
permite superar la necesidad de probar exhaustivamente todos los posibles
datos de entrada, lo cual generalmente es imposible.
¿Cómo se identifican las clases de equivalencia?
El proceso de identificación de las clases de equivalencia es un proceso
heurístico que se basa –usualmente- en la descripción de los datos de entrada
que encontramos en las especificaciones del software.
Glenford J. Myers menciona algunas pautas para identificar clases de
equivalencia:
1. ‘‘Si una condición especifica un rango de valores, se identifican
una clase de equivalencia válida y dos clases de equivalencia
inválidas”
Por ejemplo: CANTIDAD puede ser un valor entre 1 y 999. La clase
de equivalencia válida será 1 ≤ CANTIDAD ≤ 999. Una clase inválida
será CANTIDAD < 1, y otra clase inválida: CANTIDAD > 999.
2. ‘‘Si una condición especifica una cantidad de valores, se
identifican una clase de equivalencia válida y dos inválidas”
Por ejemplo: se pueden listar de 1 a 6 propietarios de un bien. La
clase de equivalencia válida será de 1 a 6 propietarios, mientras que
las dos inválidas serán: sin propietario y más de 6 propietarios.
3. ‘‘Si una condición especifica un conjunto de valores válidos, se
identifican una clase de equivalencia válida y una clase inválida”
Por ejemplo: el tipo de vehículo puede ser BUS, MICROBUS,
CAMIÓN, o AUTO. Se tendrán cuatro clases de equivalencia válidas
(los cuatro valores válidos) y una inválida (cualquier valor no
especificado, digamos “VOLQUETE”)
4. ‘‘Si una condición se especifica como debe ser, se identifican una
clase de equivalencia válida y una inválida”.
Por ejemplo: el primer carácter de un código debe ser una letra. La
clase de equivalencia válida será: es una letra; y la clase inválida: no
es una letra.
5. ‘‘Si tiene motivos para pensar que el software no manipula de
manera similar a todos los elementos de una clase de equivalencia,
entonces particiónela en clases de equivalencia más pequeñas’’
Luego de identificar las clases de equivalencia, se deberá proceder a escribir
cada caso de prueba tratando que este cubra la mayor cantidad de clases de
equivalencia válidas, hasta que todas hayan sido consideradas. Por el contrario,
cada clase de equivalencia inválida deberá ser cubierta por un caso de prueba
de manera individual, hasta que todas sean cubiertas.
Consideremos el ejemplo del ingreso a una base de datos de un código de
artículo con las características siguientes:
o Está compuesto por caracteres alfanuméricos
o El número de caracteres varía entre 4 y 8
o El primer carácter debe ser una letra
Las clases de equivalencia (CE) podrían ser:
o CE1 - El código ingresado es alfanumérico
o CE2 - El código ingresado NO es alfanumérico
o CE3 - El código ingresado tiene entre 4 y 8 caracteres
o CE4 - El código ingresado tienen menos de 4 caracteres
o CE5 - El código ingresado tiene más de 8 caracteres
o CE6 - El primer carácter es una letra
o CE7 - El primer carácter NO es una letra
Los casos de prueba para cubrir las clases de equivalencia identificadas
podrían ser:

Clases de
Clases de
Dato de prueba equivalencia
equivalencia válidas
inválidas
ABC123 CE1, CE3, CE6

AB*123 CE2

A CE4

ABCDEF123456 CE5

123ABC CE7

3.2. Análisis de Valores Límites


Esta técnica es un complemento de la anterior. Los valores límite son aquellos
que se encuentran en los “bordes” de las clases de equivalencia. La
experiencia muestra que muchos errores ocurren en el tratamiento a estos
valores límite.
Nuevamente Glenford J. Myers aporta algunos lineamientos para hacer este
análisis:
1. ‘‘Si una condición especifica un rango de valores, desarrolle casos
de prueba para los extremos del rango y para valores que estén
inmediatamente debajo y encima de los extremos inferior y superior
del rango”
Para el antes mencionado ejemplo de la CANTIDAD -que puede ser
un valor entre 1 y 999- deberían considerarse casos de prueba para
los valores 1 y 999, así como para 0 y 1000.
Si se tratara de una LONGITUD con un rango entre 5.0000 y
10.0000, tendríamos que considerar casos de prueba para los
valores 5.0000, 10.0000, 4.9999 y 10.0001.
2. ‘‘Si se especifica un conjunto ordenado, tal como una lista o una
tabla, se debe tener especial cuidado en el primer elemento del
conjunto, y en el último”
Si consideramos el ejemplo del ingreso de un código de artículo, encontramos
valores límites en la longitud del código (de 4 a 8 caracteres). En base a ello
podríamos añadir los casos de prueba siguientes:

Dato de prueba Valores límite

AB1 3

AB12 4

ABCD1234 8

ABCD12345 9

3.3. Gráficos causa-efecto


Una debilidad de la técnica de las técnicas de particiones de equivalencia y
análisis de valores límite es que no facilita la combinación de condiciones. Los
gráficos causa-efecto permiten combinar condiciones, lo cual también ayuda en
el hallazgo de ambigüedades y/o inconsistencias en las especificaciones.
Esta técnica es una adaptación de aquella utilizada para probar circuitos
lógicos digitales. En esta técnica las especificaciones del software -dadas en un
lenguaje natural- se modelan en un lenguaje formal: los gráficos causa-efecto.
Para ello, a partir de cada especificación del software deben identificarse:
o las causas – condiciones de entrada o clases de equivalencia de entrada
o los efectos – condiciones de salida o transformaciones observables
o las restricciones – limitaciones externas del software
Con estos elementos se construye un gráfico booleano:
o las causas y los efectos son los nodos del gráfico
o las causas se ubican a la izquierda del gráfico, y los efectos a la derecha
o las relaciones lógicas entre causas y efectos se grafican con vectores y
utilizando los operadores lógicos AND, OR y NOT
Los símbolos básicos para los gráficos causa efecto son los siguientes:

Luego, el gráfico es vaciado a una tabla de decisión que servirá para


desarrollar los casos de prueba necesarios. Cada causa y cada efecto tendrán
una fila en la tabla. Cada columna de la tabla representará una combinación de
causas y corresponderá a un caso de prueba.
Tomemos como ejemplo la especificación de un módulo para buscar un
carácter en una cadena: se debe ingresar la longitud de la cadena y el carácter
a buscar. Si la longitud de la cadena es mayor a 80 debe mostrarse un mensaje
de error. Si el carácter se encuentra en la cadena, debe mostrarse su posición,
de lo contrario debe mostrarse el mensaje “No encontrado”.
Las causas o condiciones de entrada serían:
C1: entero positivo entre 1 y 80
C2: carácter a buscar
Los efectos o condiciones de salida serían:
E1: Longitud fuera de rango
E2: Posición del carácter
E3: Carácter no encontrado
El gráfico causa-efecto para esta especificación, y las proposiciones que
representa sería las siguientes:

• Si C1 y C2, entonces E2

• Si C1 y no C2, entonces E3

• Si no C1, entonces E1

El paso siguiente es vaciar el gráfico en la tabla de decisión siguiendo el


procedimiento siguiente para cada efecto (“1” representa la presencia de la
causa o efecto, “0” representa su ausencia, y “-“ indica que no se considera):
o Seleccione un efecto
o Retroceda a través del gráfico y encuentre todas las combinaciones de
causas que hagan que el efecto ocurra
o Cree una columna en la tabla de decisión para cada combinación de
causas
o Determine, para cada combinación, los estados de los demás efectos y
colóquelos en la columna
Para este sencillo ejemplo, la tabla de decisión sería la siguiente:

Prueba 1 Prueba 2 Prueba 3

C1 1 1 0

C2 1 0 -

E1 0 0 1

E2 1 0 0
E3 0 1 0

3.4. Conjetura de errores


Esta técnica consiste en que la(s) persona(s) que diseñan los casos de prueba
enumere posibles errores, en base a su experiencia e –incluso- su intuición. Es
un proceso ad-hoc, por lo que no hay un procedimiento establecido para hacer
conjeturas. Sin embargo, si se tienen documentados los defectos de las
versiones anteriores, o de programas similares, esta documentación será una
fuente muy valiosa para enriquecer la conjetura de errores.
Ejemplos típicos son los casos en que puede ocurrir una división entre cero, la
manipulación de arreglos y de punteros más allá de sus límites, o las
condiciones del entorno del software.
4. TECNICAS DE CAJA TRANSPARENTE
Al contrario del enfoque de Caja Negra, en este caso las técnicas se basan en
el conocimiento de la estructura interna del software. El objetivo es asegurar
que los componentes del programa están trabajando adecuadamente, por lo
que se debe conocer el código o el seudo código del software para forzar la
ejecución de alternativas específicas del software (bifurcaciones, por ejemplo),
Éstas técnicas son útiles para revelar defectos de lógica, control de condiciones,
secuenciación e inicializaciones.
4.1. Cobertura lógica
Al analizar la estructura lógica interna –aún de programas relativamente
sencillos- nos damos cuenta que el número de las posibles secuencias lógicas
de ejecución es muy grande, lo cual hace –nuevamente- materialmente
imposible realizar pruebas exhaustivas que cubran todas las posibilidades. Por
ello, se requiere un criterio para determinar cuántos casos de prueba serán
necesarios para considerar que el software ha sido suficientemente probado.
Este criterio se le conoce como criterio de cobertura.
El criterio de cobertura más simple e intuitivo podría ser: diseñar los casos de
prueba necesarios para que se ejecute por lo menos una vez cada sentencia
del programa. Pero para un programa como el del código mostrado, este
criterio es muy débil. Basaría con hacer a = 2, b = 0 y x = 3 para cumplir con este
criterio. Sin embargo, podrían pasarse por alto defectos en el sentido de los
operadores lógicos al codificar x < 1 en vez de x > 1, por ejemplo.
public void foo (int a, int b, int x) {
if (a > 1 && b == 0) {
x = x / a;
}
if (a == 2 || x > 1) {
x = x + 1;
}
}
Un criterio de cobertura bastante completo es el denominado Cobertura de
Condición Múltiple que señala que deben diseñarse los casos de prueba
necesarios para que se invoquen por lo menos una vez cada una de las
combinaciones posibles de los resultados de condición en cada decisión. Y si
se trata de programas con múltiples puntos de entrada, habría que agregar y
que se invoquen por lo menos una vez cada uno de los puntos de entrada.
Por ejemplo, para probar el programa anterior de manera adecuada se
deberían considerar ocho combinaciones:

1. a > 1, b = 0 5. a = 2, x > 1

2. a > 1, b ≠ 0 6. a = 2, x ≤ 1

3. a ≤ 1, b = 0 7. a ≠ 2, x > 1

4. a ≤ 1, b ≠ 0 8. a ≠ 2, x ≤ 1

En este ejemplo, las ocho combinaciones podrían ser cubiertas con cuatro
casos de prueba:

1. a = 2, b = 0, x = 4

2. a = 2, b = 1, x= 1

3. a = 1, b = 0, x = 2

4. a = 1, b = 1, x = 1

4.2. Flujo de datos


Esta técnica se basa en el rol de las variables dentro del programa y es útil
para detectar defectos debido a la definición, inicialización y/o uso de las
mismas. Por su naturaleza, la eficacia de esta técnica decae conforme el
tamaño del programa aumenta, por lo que se recomienda para programas
pequeños y complejos.
Las variables de un programa pueden jugar diferentes roles en determinadas
sentencias: se les puede asignar un valor como en read x, o en x = y + 10; pueden
usarse como un predicado como en if x > 100; o para hacer un cálculo como en y
= 25 * x. A las sentencias de asignación se les denominará de definición (def),
mientras que a las otras se les denominará de uso (uso)
Esta técnica trata de analizar el flujo de datos desde las sentencias de
asignación hasta aquellas donde se usan las variables. Para ello se debe
identificar y clasificar las ocurrencias de cada variable, ya que durante las
pruebas se deben ejecutar todas las asignaciones y todos los usos.
En el ejemplo siguiente se tienen cuatro variables cuenta, i, n y numero.
1 cuenta = 0
2 read (n)
3 i=1
4 while (i <= n)
5 read (numero)
6 cuenta = cuenta + 1
7 i=i+1
8 end while
9 print (cuenta)
El siguiente paso es tabular las ocurrencias de definición y uso de cada
variable. En las tablas siguientes se especifica la línea de código en la que
aparece la variable y un número secuencial para cada flujo. Las variables
podrían ser definidas y usadas en la misma sentencia.

cuenta i n numero
def uso def uso def uso def uso
1 1 6 5 3 4 9 2 4 10 5 6
2 1 9 6 3 7
3 6 5 7 7 7
4 6 9 8 7 4
Luego deben generarse los datos de prueba para ejecutar todos los flujos
tabulados. Para este ejemplo bastarían dos casos de prueba:

Datos de prueba Flujos


n=0 2, 5, 9
n = 3; numero =1,2,3 1, 3, 4, 5, 6, 7, 8, 9, 10
4.3. Prueba de lazos (loops)
Los lazos o “loops” son estructuras de programación a las que frecuentemente
están asociados diversos defectos del software, por lo que merecen una
especial atención.
Para un lazo simple que puede variar de cero a n iteraciones, se deben
considerar casos de prueba para:
o Cero iteraciones (el lazo no se ejecuta)
o Una iteración
o Dos iteraciones
o k iteraciones (k < n)
o n – 1 iteraciones
o n + 1 iteraciones (si es posible)
o Valor negativo para la variable de control del lazo
Si el mínimo de iteraciones fuera mayor a cero, pruebe con un número de
iteraciones menor al mínimo.
Para los lazos anidados los casos de prueba pueden crecer exponencialmente.
Para limitar este número de casos considere lo siguiente:
o Empiece con el nivel más interno, poniendo los niveles externos en sus
valores mínimos.
o Pruebe el mínimo, el mínimo + 1, un número típico, el máximo – 1 y el
máximo número de iteraciones para el nivel más interno.
o Suba un nivel, establezca un número típico de iteraciones para el nivel
interno, repita el paso anterior para el presente nivel, manteniendo los
niveles externos en sus valores mínimos.
o Continúe con este proceso hasta probar todos los niveles del lazo
anidado.
5. CONCLUSIONES
o La prueba de software tiene el objetivo de encontrar defectos, por lo que
deben ser realizadas por una persona, o un equipo de personas,
independiente del desarrollador del software.
o Realizar todas las pruebas posibles generalmente es imposible, por
limitaciones de tiempo y de recursos materiales. La prueba de software
consistirá en diseñar y ejecutar un número limitado de casos de prueba
que permita encontrar el máximo número de defectos.
o La prueba de software usualmente requiere utilizar una combinación de
técnicas de caja negra y de caja transparente para lograr un conjunto de
casos de prueba consistente, combinación que dependerá de las
características del software y de las limitaciones del entorno
6. REFERENCIAS BIBLIOGRÁFICAS
• Beizer, Boris; Software Testing Techniques - Second Edition; The
Coriolis Group; 1990
• Black, Rex; Pragmatic Software Testing; John Wiley & Sons; 2007
• Burnstein, Ilene; Practical software testing; Springer-Verlag New York
Inc.; 2003
• Harinath P. V. y otros; Software Testing Guide Book; Software Testing
Research Lab; 2004
• Myers, Glenford J.; The Art of Software Testing-Second Edition; John
Wiley & Sons, Inc.; 2004
• Rojas, Johanna y Barrios, Emilio; Investigación sorbe el estado del arte
en diseño y aplicación de pruebas de software; Universidad Francisco
José de Caldas - Bogotá; 2007

You might also like