Introducción
Las ideas de prueba se basan en modelos de anomalía, nociones de qué anomalías son plausibles en software y cómo se
pueden descubrir estas anomalías. En esta directriz se muestra como crear las ideas de prueba de expresiones booleanas
y relacionales. Primero motiva las técnicas mirando el código, luego describe cómo aplicarlas si el código todavía no
se ha escrito o si no está disponible.
Expresiones booleanas
Tenga en cuenta el extracto de código siguiente, tomado de un sistema (imaginario) para gestionar la detonación de
bombas. Forma parte del sistema de seguridad y controla si se obedece el pulsador del botón "detonar bomba".
si (publicIsClear || technicianClear) {
bomb.detonate();
}
El código es incorrecto. || debería ser &&. Este error tendrá
efectos negativos. En lugar de detonar la bomba cuando ambos, el técnico de bombas y el público, estén fuera de
alcance, el sistema detonará cuando cualquiera de los dos esté fuera de alcance.
¿Qué prueba encontraría este error?
Imagine una prueba en que el botón se pulsa cuando el técnico y el público estén fuera del alcance. El código
permitiría detonar la bomba. Pero -y esto es importante - el código correcto (el que utiliza un &&) haría lo mismo. Entonces la prueba no sirve para encontrar esta anomalía.
De forma similar, este código incorrecto se comporta correctamente cuando el técnico y el público están junto a la
bomba: la bomba no se detona.
Para encontrar el error, debe tener un caso en que el código escrito se evalúe diferentemente del código que debería
estar escrito. Por ejemplo el público debe estar fuera de alcance, per el técnico de bombas sigue junto a la bomba.
Estas son todas las pruebas en forma de tabla:
publicIsClear
|
technicianClear
|
Código según escrito...
|
El código correcto tendría...
|
|
true
|
true
|
detona
|
detonado
|
la prueba es inútil (para esta anomalía)
|
true
|
false
|
detona
|
no detonado
|
prueba útil
|
false
|
true
|
detona
|
no detonado
|
prueba útil
|
false
|
false
|
no detona
|
no detonado
|
la prueba es inútil (para esta anomalía)
|
Las dos pruebas intermedias son útiles para encontrar este error concreto. Tenga en cuenta, sin embargo, que son
redundantes: como cualquiera de las dos encontrará el error, no es necesario que ejecute las dos.
Existen otros modos en que la expresión puede ser incorrecta. Estas son dos listas de errores comunes en expresiones
booleanas. Las anomalías de la izquierda se han capturado con la técnica que se discute aquí. Las anomalías de la
derecha pueden no aparecer. Por lo tanto, esta técnica no captura todas las anomalías que deseamos, pero sigue siendo
útil.
Anomalías seleccionadas
|
Anomalías que posiblemente no se detectarán
|
Utilización del operador incorrecto: una || b debería ser a&&b
|
Variable incorrecta utilizada: a&&b&&c debería ser
a&& x&&d
|
La negación se omite o es incorrecta: a||b debería ser !a||b, o
! a||b debería ser a||b
|
Expresión demasiado sencilla: a&&b debería ser a&&b&&c
|
La expresión entre paréntesis está configurada de forma incorrecta: &&b||c debería ser
&&(b||c)
|
Las expresiones con más de una de las anomalías de la columna izquierda
|
La expresión es demasiado compleja: a&&b&&c debería ser a&&b
(Esta anomalía es poco probable, pero fácil de detectar con pruebas útiles por otros motivos).
|
|
¿Cómo se utilizan estas ideas? Imagine que tiene una expresión booleana como a&&!b.
Puede construir una tabla de la verdad como la siguiente:
a
|
b
|
a&&!b
(código según escrito)
|
quizás debería ser
a||!b
|
quizás debería ser
!a&&!b
|
quizás debería ser
a&&b
|
...
|
true
|
true
|
false
|
true
|
false
|
true
|
...
|
true
|
false
|
true
|
true
|
false
|
false
|
...
|
false
|
true
|
false
|
false
|
false
|
false
|
...
|
false
|
false
|
false
|
true
|
true
|
false
|
...
|
Si ha revisado todas las posibilidades, verá que la primera, la segunda y la cuarta posibilidad son todas las
necesarias. La tercera expresión no encontrará anomalías que no encuentren las demás, así que no necesita intentarla.
(A medida que las expresiones sean más complicadas, los ahorros debidos a casos innecesarios aumentarán rápidamente).
Por supuesto, nadie sano construiría una tabla como esta. Por suerte, no tendrá que hacerlo. Es fácil de recordar los
casos necesarios para expresiones simples. (Consulte la sección siguiente). Para expresiones más complejas, como A&&B||C, consulte Ideas de pruebas para combinaciones de AND y OR, que lista ideas de prueba para
expresiones con dos o tres operadores. Para expresiones más complejas, un programa se puede utilizar para generar ideas
de prueba.
Tablas para expresiones booleanas simples
Si la expresión es A&&B, pruebe con:
A
|
B
|
true
|
true
|
true
|
false
|
false
|
true
|
Si la expresión es A||B, pruebe con:
A
|
B
|
true
|
false
|
false
|
true
|
false
|
false
|
Si la expresión es A1 && A2 && ... &&
An, pruebe con:
A1, A2, ..., y An son todos true
|
A1 es false, el resto son true
|
A2 es false, el resto son true
|
...
|
An es false, el resto son true
|
Si la expresión es A1 || A2 || ... || An, pruebe con:
A1, A2, ..., y An son todos false
|
A1 es true, el resto son false
|
A2 es true, el resto son false
|
...
|
An es true, el resto son false
|
Si la expresión es A, pruebe con:
Por lo tanto, cuando necesite probar a&&!b, Puede aplicar la primera tabla anterior,
invirtiendo el sentido de b (porque está negado), y obtener esta lista de Ideas de
prueba:
-
A true, B false
-
A true, B true
-
A false, B false
Expresiones relacionales
Este es otro ejemplo de código con una anomalía:
if (finished < required) {
siren.sound();
}
< debería ser un <=. Estos errores suelen ser habituales. Igual que
en las expresiones booleanas, puede construir una tabla de valores de prueba y ver cuales detecta la anomalía:
finalizado
|
necesario
|
código según escrito...
|
el código correcto tendría...
|
1
|
5
|
suena la sirena
|
sonaba la sirena
|
5
|
5
|
no suena la sirena
|
sonaba la sirena
|
5
|
1
|
no suena la sirena
|
no sonaba la sirena
|
Más generalmente, la anomalía se puede detectar siempre que finished=required. En los análisis
de anomalías plausibles, podemos obtener estas normas para ideas de prueba:
Si la expresión es A<B o A>=B, pruebe con lo siguiente:
A=B
|
A ligeramente menos que B
|
Si la expresión es A>B o A<=B, pruebe con lo siguiente:
A=B
|
A ligeramente más que B
|
¿Qué significa "ligeramente"? Si A y B son enteros, A debe ser uno menos o más que B. Si son números de punto flotante,
A debe ser un número cercano a B. (Probablemente no es necesario que sea el número de punto flotante más cercano a B).
Reglas para expresiones booleanas y relacionales
combinadas
La mayoría de operadores relacionales ocurren dentro de expresiones booleanas, como en este ejemplo:
if (finished < required) {
siren.sound();
}
Las reglas de las expresiones relacionales conducirían a estas ideas de prueba:
-
finalizado equivale a necesario
-
finalizado es ligeramente menos que esta expresión: necesario
Las reglas de las expresiones booleanas conducirían a lo siguiente:
-
finalizado < necesario debería ser true
-
finalizado < necesario debería ser false
Puede ser que esta expresión: finalizado es ligeramente menos que esta expresión: necesario, finalizado < necesario es true, así que no es necesario escribir
la última.
Si esta expresión es false no es necesario escribirla: finalizado equivale necesario, , finalizado < necesario .
Si una expresión relacional no contiene operadores booleanos (&& and ||), omita el hecho de que también es una expresión booleana.
Las cosas son más complicadas con combinaciones de operadores booleanos y relacionales, como el siguiente:
if (count<5 || always) {
siren.sound();
}
Desde la expresión relacional, obtiene:
-
count ligeramente menos que 5
-
count igual a 5
Desde la expresión booleana, obtiene:
-
count<5 true, siempre false
-
count<5 false, siempre true
-
count<5 false, siempre false
Esto se puede combinar en tres ideas de prueba más específicas. (Aquí, tenga en cuenta que count
es un entero).
-
count=4, siempre false
-
count=5, siempre true
-
count=5, siempre false
Tenga en cuenta que count=5 se utiliza dos veces. Puede parecer mejor utilizarlo sólo una vez;
para permitir el uso de algún otro valor - después de todo, por qué probar esta expresión con 5 dos veces: count ? ¿No sería mejor intentarlo una vez con 5 y otra con algún otro valor para que el resultado sea
false? (Ejemplo: count<5 Lo sería, pero es peligroso intentarlo. Esto es porque es fácil
equivocarse. Imagine que ha intentado lo siguiente:
-
count=4, siempre false
-
count=5, siempre true
-
count<5 false, siempre false
Imagine que existe una anomalía que sólo se puede capturar con el valor siguiente: count=5. Esto
significa que el valor 5 producirá "false" en la expresión count<5, cuando el código correcto
habría producido true. Sin embargo, este valor false se convierte inmediatamente en OR con el valor de siempre, que es
true. Esto significa que el valor de toda la expresión es correcto, aunque el valor de la subexpresión relacional fuera
incorrecto. La anomalía no se descubrirá.
La anomalía se descubre si es el otro count=5 que se deja menos específico.
Ocurren problemas similares cuando la expresión relacional se encuentra a la derecha del operador booleano.
Como resulta complicado conocer qué subexpresiones deben ser exactas y cuáles pueden ser generales, es mejor hacerlas
todas exactas. La alternativa es utilizar el programa de expresión booleana mencionado anteriormente. Produce ideas de prueba correctas para
expresiones booleanas y relacionales combinadas.
Ideas de prueba sin código
Tal como se explica en Concepto:
Diseño de primera prueba, suele ser preferible diseñar las pruebas antes de implementar código. Así que, aunque las
técnicas se motivan con ejemplos de códigos, se aplicarán habitualmente sin código. ¿Cómo?
Ciertos artefactos de diseño, como gráficos de estado y diagramas de secuencia, utilizan las expresiones booleanas como
vigilancias. Estos casos son directos: simplemente añaden ideas de prueba de las expresiones booleanas a la lista de
comprobación de ideas de prueba del artefacto. Consulte el apartado Directriz de producto de trabajo: Ideas de prueba para gráficos de estado y diagramas de
actividad.
El caso más complica aparece cuando las expresiones booleanas son implícitas en lugar de explícitas. Esto suele ocurrir
en las descripciones de las API. A continuación se muestra un ejemplo. Imagine este método:
List matchList(Directory d1, Directory d1,
FilenameFilter excluder);
La descripción del comportamiento de este método sería la siguiente:
Devuelve una lista de los nombres de vía de acceso absolutos de todos los archivos que aparecen en ambos
directorios. Los subdirectorios descienden. [...] Nombres de archivo que coinciden con el excluyente se excluyen de la lista devuelta. El excluyente sólo se aplica a los directorios de
nivel superior, no a los nombres de archivo de los subdirectorios.
Las palabras "and", "or" no aparecen. ¿Pero cuando es un nombre de archivo incluido en la lista devuelta? Cuando
aparece en el primer directorio y aparece en el segundo directorio y se encuentra en un directorio de
nivel inferior o no se excluye específicamente. En el código:
if (appearsInFirst && appearsInSecond &&
(inLowerLevel || !excluded)) {
add to list
}
Estas son las ideas de prueba para esta expresión, en forma de tabla:
appearsInFirst
|
appearsInSecond
|
inLower
|
excluded
|
true
|
true
|
false
|
true
|
true
|
true
|
false
|
false
|
true
|
true
|
true
|
true
|
true
|
false
|
false
|
false
|
false
|
true
|
false
|
false
|
El enfoque general para descubrir expresiones booleanas implícitas a partir del texto es listar primero las acciones
descritas (como "devuelve un nombre coincidente"). A continuación, escriba la expresión booleana que describe los casos
en que se realiza alguna acción. Derive las ideas de prueba de todas las expresiones.
Los desacuerdos tienen cabida en este proceso. Por ejemplo, una persona puede escribir la expresión booleana que se ha
utilizado anteriormente. Otra puede decir que realmente hay dos acciones distintas: primero, el programa descubre los
nombres coincidentes y, a continuación, los filtra. Por lo tanto, en lugar de una expresión, hay dos:
-
descubrir una coincidencia:
-
ocurre cuando un archivo está en el primer directorio y un archivo con el mismo nombre está en el segundo
directorio
-
filtrar una coincidencia:
-
ocurre cuando los archivos coincidentes se encuentran en el nivel superior y el nombre coincide con el excluyente
Estas diferencias pueden llevar a diferentes ideas de prueba y, por lo tanto, a diferentes pruebas. Pero las
diferencias es más probable que no sean especialmente importantes. Es decir, el tiempo que se dedica a preocuparse
sobre si la expresión es correcta, probando alternativas, sería mejor dedicarlo a otras técnicas y a producir más
pruebas. Si tiene curiosidad sobre los tipos de diferencias, continúe leyendo.
La segunda personas obtendrá dos conjuntos de ideas de prueba.
ideas de prueba sobre el descubrimiento de una coincidencia:
-
archivo en el primer directorio, archivo en el segundo directorio (true, true)
-
archivo en el primer directorio, no hay archivo en el segundo directorio (true, false)
-
no hay archivo en el primer directorio, archivo en el segundo directorio (false, true)
ideas de prueba sobre el filtrado de una coincidencia (una vez que se haya descubierto):
-
los archivos coincidentes se encuentran en el nivel superior, el nombre coincide con el excluyente (true, true)
-
los archivos coincidentes se encuentran en el nivel superior, el nombre no coincide con el excluyente (true, false)
-
los archivos coincidentes se encuentran en algún nivel inferior, el nombre coincide con el excluyente (false, true)
Imagine que se combinan estos dos conjuntos de ideas de prueba. Los del segundo conjunto sólo importan cuando el
archivo está en ambos directorios, así que sólo se pueden combinar con la primera idea del primer conjunto. Esto nos da
lo siguiente:
archivo en el primer directorio
|
archivo en el segundo directorio
|
nivel superior
|
coincide con el excluyente
|
true
|
true
|
true
|
true
|
true
|
true
|
true
|
false
|
true
|
true
|
false
|
true
|
Dos de las ideas de prueba sobre el descubrimiento de una coincidencia no aparecen en esta tabla. Podemos añadirlas del
modo siguiente:
archivo en el primer directorio
|
archivo en el segundo directorio
|
nivel superior
|
coincide con el excluyente
|
true
|
true
|
true
|
true
|
true
|
true
|
true
|
false
|
true
|
true
|
false
|
true
|
true
|
false
|
-
|
-
|
false
|
true
|
-
|
-
|
Las celdas en blanco indican que las columnas son irrelevantes.
Esta tabla ahora es parecida a la tabla de la primera persona. El parecido se puede enfatizar mediante la misma
terminología. La tabla de la primera persona tiene una columna denominada "inLower", y la segunda persona tiene una
denominada "en el nivel superior". Se pueden convertir invirtiendo el sentido de los valores. De este modo, obtenemos
esta versión de la segunda tabla:
appearsInFirst
|
appearsInSecond
|
inLower
|
excluded
|
true
|
true
|
false
|
true
|
true
|
true
|
false
|
false
|
true
|
true
|
true
|
true
|
true
|
false
|
-
|
-
|
false
|
true
|
-
|
-
|
Las tres primeras filas son idénticas a la tabla de la primera persona. Las dos últimas difieren sólo en que esta
versión no especifica valores que especifica la primera. Esto equivale a una suposición sobre el modo en que se
escribió el código. La primera asume una expresión booleana complicada:
if (appearsInFirst && appearsInSecond &&
(inLowerLevel || !excluded)) {
add to list
}
La segunda asume expresiones booleanas anidadas:
if (appearsInFirst && appearsInSecond) {
// found match.
if (inTopLevel && excluded) {
// filter it
}
}
La diferencia entre las dos es que las ideas de prueba de la primera detectan dos anomalías que las ideas de la segunda
no detectan, porque esas anomalías no son aplicables.
-
En la primera implementación, puede existir un error en los paréntesis. ¿Los paréntesis que rodean a || son correctos o incorrectos? Como en la segunda implementación no hay paréntesis ni ||, la anomalía no puede existir.
-
Los requisitos de prueba de la primera implementación comprueban si la segunda expresión, &&debería ser ||. En la segunda implementación, que detalla && se reemplaza con el explícito &&. No hay ningún error
||-for-&& , per se. (Puede ocurrir que el anidado sea
incorrecto, pero esta técnica no lo soluciona).
|