Cobertura del código
En resumen: La cobertura de sentencias pregunta si se ejecutó cada línea de código. La cobertura de ramas pregunta si cada decisión fue por los dos lados. Para la función del envío la primera necesita 2 pruebas, la segunda 3. Un 100 % de cobertura de ramas incluye el 100 % de cobertura de sentencias, pero no al revés. Y ninguna cobertura dice que se comprobó el resultado ni que el límite está en el lugar correcto.
Problema
100 % de cobertura, una tarifa equivocada
El programador ejecuta las pruebas del envío con una herramienta que cuenta qué líneas se ejecutaron. Las pruebas son dos: 0 € y 60 €. El informe dice: 100 % de las líneas.
Al día siguiente los clientes con un carrito de 30 € pagan 4,99 € de envío en lugar de 4,90 €. La línea con la tarifa sí se ejecutó. Ninguna prueba vio lo que había en ella.
La idea
La caja blanca
Hasta ahora las pruebas salían de la regla: esa es la caja negra. Las técnicas de caja blanca sacan las pruebas del código: qué líneas tiene y qué caminos puede tomar el programa.
Para esta lección no necesitas saber escribir código. Necesitas saber leer la función del envío. El lenguaje lo eliges tú, en las pestañas:
static String delivery(double total) { if (total <= 0) { return "rechazado"; } double fee = 4.90; if (total >= 50) { fee = 0; } return String.format("%.2f €", fee);}static string Delivery(double total){ if (total <= 0) { return "rechazado"; } double fee = 4.90; if (total >= 50) { fee = 0; } return $"{fee:F2} €";}def delivery(total): if total <= 0: return "rechazado" fee = 4.90 if total >= 50: fee = 0 return f"{fee:.2f} €"function delivery(total) { if (total <= 0) { return "rechazado"; } let fee = 4.9; if (total >= 50) { fee = 0; } return `${fee.toFixed(2)} €`;}La función tiene dos decisiones (lugares donde el programa elige un camino): «el total es 0 o menos» y «el total es 50 o más». La segunda es un «if» sin «else»: si es falsa, el programa simplemente se salta la línea que pone la tarifa a cero.
La idea
Cobertura de sentencias
La cobertura de sentencias pregunta: ¿cuántas de las líneas que hacen algo ha ejecutado al menos una prueba?
Aquí están las dos pruebas del principio, como llamadas a la función, con su salida:
System.out.println("0 € → " + delivery(0));System.out.println("60 € → " + delivery(60));0 € → rechazado60 € → 0.00 €Console.WriteLine("0 € → " + Delivery(0));Console.WriteLine("60 € → " + Delivery(60));0 € → rechazado60 € → 0.00 €print("0 € →", delivery(0))print("60 € →", delivery(60))0 € → rechazado60 € → 0.00 €console.log("0 € → " + delivery(0));console.log("60 € → " + delivery(60));0 € → rechazado60 € → 0.00 €- 0 € pasa por la primera comprobación y devuelve «rechazado».
- 60 € pasa por todo lo demás: la tarifa pasa a ser 4,90, luego la comprobación «desde 50» la deja en 0, luego el retorno.
Se ejecutó cada línea. La cobertura de sentencias es del 100 %, con dos pruebas.
La idea
Cobertura de ramas
La cobertura de ramas pregunta otra cosa: ¿ha ido cada decisión por los dos lados?
Aquí hay cuatro ramas: la primera comprobación verdadera y falsa, la segunda verdadera y falsa. 0 € toma la primera como verdadera. 60 € toma la primera como falsa y la segunda como verdadera. Nadie toma la segunda como falsa: ese es el camino vacío de un «if» sin «else». La cobertura es de 3 de 4, es decir, 75 %.
Una tercera prueba, con envío de pago, cierra el hueco:
System.out.println("0 € → " + delivery(0));System.out.println("30 € → " + delivery(30));System.out.println("60 € → " + delivery(60));0 € → rechazado30 € → 4.90 €60 € → 0.00 €Console.WriteLine("0 € → " + Delivery(0));Console.WriteLine("30 € → " + Delivery(30));Console.WriteLine("60 € → " + Delivery(60));0 € → rechazado30 € → 4.90 €60 € → 0.00 €print("0 € →", delivery(0))print("30 € →", delivery(30))print("60 € →", delivery(60))0 € → rechazado30 € → 4.90 €60 € → 0.00 €console.log("0 € → " + delivery(0));console.log("30 € → " + delivery(30));console.log("60 € → " + delivery(60));0 € → rechazado30 € → 4.90 €60 € → 0.00 €Tres pruebas, 100 % de las ramas. Y la respuesta a la pregunta de la lección: el 100 % de cobertura de ramas de la función del envío necesita 3 pruebas.
Más precisamente, para curiosos
ISTQB CTFL v4.0.1 describe las dos medidas en la sección 4.3: pruebas de sentencias y pruebas de ramas. La cobertura es el número de sentencias ejecutadas (o ramas tomadas) sobre el total, en porcentaje. El plan de estudios también establece cómo se relacionan: el 100 % de cobertura de ramas incluye el 100 % de cobertura de sentencias, pero no al revés.
Hay medidas más estrictas: la cobertura de condiciones (cada condición dentro de una decisión se ha visto verdadera y falsa) y MC/DC (cada condición ha mostrado que por sí sola puede cambiar la decisión). MC/DC se exige para el software más crítico de la aviación. No están en CTFL v4.0.
La cobertura se mide con una herramienta: JaCoCo para Java, coverlet para C#, coverage.py para Python, c8 o la integrada en Node para JavaScript. Se ejecutan las pruebas, y la herramienta marca qué líneas y ramas se tomaron.
Pruébalo
¿Cuántas pruebas?
La idea
Por qué el 100 % de sentencias perdió la tarifa
Aquí está la función del principio, con la tarifa de 4,99:
static String delivery(double total) { if (total <= 0) { return "rechazado"; } double fee = 4.99; if (total >= 50) { fee = 0; } return String.format("%.2f €", fee);}static string Delivery(double total){ if (total <= 0) { return "rechazado"; } double fee = 4.99; if (total >= 50) { fee = 0; } return $"{fee:F2} €";}def delivery(total): if total <= 0: return "rechazado" fee = 4.99 if total >= 50: fee = 0 return f"{fee:.2f} €"function delivery(total) { if (total <= 0) { return "rechazado"; } let fee = 4.99; if (total >= 50) { fee = 0; } return `${fee.toFixed(2)} €`;}Con las dos pruebas de sentencias la salida es la misma que con la función correcta:
System.out.println("0 € → " + delivery(0));System.out.println("60 € → " + delivery(60));0 € → rechazado60 € → 0.00 €Console.WriteLine("0 € → " + Delivery(0));Console.WriteLine("60 € → " + Delivery(60));0 € → rechazado60 € → 0.00 €print("0 € →", delivery(0))print("60 € →", delivery(60))0 € → rechazado60 € → 0.00 €console.log("0 € → " + delivery(0));console.log("60 € → " + delivery(60));0 € → rechazado60 € → 0.00 €A 60 € la línea con la tarifa se ejecuta, pero justo después la tarifa pasa a ser 0. El valor equivocado nunca llega a la salida. Ejecutar no es comprobar.
La tercera prueba, la de la cobertura completa de ramas, toma el camino vacío y muestra la tarifa:
System.out.println("0 € → " + delivery(0));System.out.println("30 € → " + delivery(30));System.out.println("60 € → " + delivery(60));0 € → rechazado30 € → 4.99 €60 € → 0.00 €Console.WriteLine("0 € → " + Delivery(0));Console.WriteLine("30 € → " + Delivery(30));Console.WriteLine("60 € → " + Delivery(60));0 € → rechazado30 € → 4.99 €60 € → 0.00 €print("0 € →", delivery(0))print("30 € →", delivery(30))print("60 € →", delivery(60))0 € → rechazado30 € → 4.99 €60 € → 0.00 €console.log("0 € → " + delivery(0));console.log("30 € → " + delivery(30));console.log("60 € → " + delivery(60));0 € → rechazado30 € → 4.99 €60 € → 0.00 €Por eso la cobertura de ramas es más fuerte. El camino vacío de un «if» es justo donde un valor de la línea de arriba llega al cliente.
La idea
Lo que la cobertura no dice
Aquí está la comprobación del límite del carrito: añadir se rechaza si es menos de 1 unidad, o si el carrito tendría más de 10.
static boolean canAdd(int inCart, int add) { if (add < 1 || inCart + add > 10) { return false; } return true;}static bool CanAdd(int inCart, int add){ if (add < 1 || inCart + add > 10) { return false; } return true;}def can_add(in_cart, add): if add < 1 or in_cart + add > 10: return False return Truefunction canAdd(inCart, add) { if (add < 1 || inCart + add > 10) { return false; } return true;}Dos llamadas dan el 100 % de las ramas: (5, 0) pasa por «rechazado», (2, 3) por «aceptado».
System.out.println(canAdd(5, 0));System.out.println(canAdd(2, 3));falsetrueConsole.WriteLine(CanAdd(5, 0));Console.WriteLine(CanAdd(2, 3));FalseTrueprint(can_add(5, 0))print(can_add(2, 3))FalseTrueconsole.log(canAdd(5, 0));console.log(canAdd(2, 3));falsetrueAhora cambia «> 10» por «> 11». Las mismas dos pruebas pasan, la cobertura sigue siendo del 100 %, y la unidad número 11 entra en el carrito. La cobertura mide lo que se ejecutó, no lo cerca que se estuvo del límite. La prueba (10, 1) de la lección de valores límite atrapa el error.
Y algo que ninguna cobertura puede ver: el código que falta. Si la comprobación del carrito vacío nunca se escribió, no queda ninguna línea sin cubrir. El 100 % de cobertura de un código que no hace lo que hace falta sigue siendo el 100 %. Por eso la caja blanca no sustituye a la caja negra. La cobertura dice dónde no hay pruebas, no que las pruebas sean buenas.
Ejercicio
La cobertura del límite
1. Dos condiciones, una decisión. Las preguntas son sobre la comprobación del límite de arriba.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Por qué pronto · Particiones de equivalencia · Valores límite · Tablas de decisión
- Continúa en: Pruebas basadas en la experiencia
- Ejercicios sobre el tema: Técnicas de diseño de pruebas