Directriz: Ideas de pruebas para booleanos y límites
Ideas de prueba basadas en anomalías de software plausibles y en cómo descubrir dichas anomalías. En esta directriz se explica cómo desarrollar ideas de prueba para expresiones booleanas y condiciones de límite.
Relaciones
Elementos relacionados
Descripción principal

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:

A

true

false


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:

  1. finalizado equivale a necesario
  2. finalizado es ligeramente menos que esta expresión: necesario

Las reglas de las expresiones booleanas conducirían a lo siguiente:

  1. finalizado < necesario debería ser true
  2. 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).

  1. count=4, siempre false
  2. count=5, siempre true
  3. 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:

  1. count=4, siempre false
  2. count=5, siempre true
  3. 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.

  1. 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.
  2. 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).