¿Qué Son Cobertura de Sentencias y Decisiones?

Cobertura de sentencias y decisiones son técnicas de test design de caja blanca (basadas en estructura) que miden qué tan exhaustivamente los test cases ejercitan el código fuente. A diferencia de las técnicas de caja negra que se enfocan en requerimientos, estas se enfocan en la estructura del código.

Por Qué Importa la Cobertura de Código

Código que nunca se ejecuta durante el testing es código que nunca se verifica. Las métricas de cobertura te dicen:

  • Qué líneas de código tus tests realmente ejecutan
  • Qué branches de puntos de decisión permanecen sin probar
  • Dónde agregar tests para mejor cobertura estructural

Cobertura de Sentencias

Definición: El porcentaje de sentencias ejecutables ejecutadas por la suite de tests.

Cobertura de Sentencias = (Sentencias Ejecutadas / Total Sentencias Ejecutables) x 100%

Ejemplo:

def calcular_descuento(precio, es_miembro):   # Línea 1
    descuento = 0                               # Línea 2
    if es_miembro:                              # Línea 3
        descuento = precio * 0.10               # Línea 4
    precio_final = precio - descuento           # Línea 5
    return precio_final                         # Línea 6

Test case 1: calcular_descuento(100, True) → Ejecuta líneas 1, 2, 3, 4, 5, 6

Cobertura de sentencias = 6/6 = 100%

Pero solo probamos con es_miembro=True. Nunca probamos cuando es falso. Esta es la debilidad de la cobertura de sentencias.

Cobertura de Decisiones (Branch Coverage)

Definición: El porcentaje de resultados de decisiones (branches verdadero/falso) ejecutados.

Cobertura de Decisiones = (Resultados de Decisiones Ejecutados / Total Resultados) x 100%

Para el mismo código:

  • Decisión en línea 3: es_miembro → tiene 2 resultados: True y False

Test case 1: calcular_descuento(100, True) → Decisión True Test case 2: calcular_descuento(100, False) → Decisión False

Cobertura de decisiones = 2/2 = 100%

flowchart TD A[Inicio] --> B[descuento = 0] B --> C{es_miembro?} C -->|True ✅ TC1| D[descuento = precio * 0.10] C -->|False ✅ TC2| E[precio_final = precio - descuento] D --> E E --> F[return precio_final]

Relación: Cobertura de Decisiones Subsume Cobertura de Sentencias

100% cobertura de decisiones → 100% cobertura de sentencias (siempre verdadero) 100% cobertura de sentencias → 100% cobertura de decisiones (NO siempre verdadero)

Cálculo de Cobertura: Paso a Paso

def categorizar_edad(edad):              # S1
    if edad < 0:                          # D1
        return "Inválida"                 # S2
    elif edad < 18:                       # D2
        return "Menor"                    # S3
    elif edad < 65:                       # D3
        return "Adulto"                   # S4
    else:                                 # D3-false
        return "Senior"                   # S5

Test cases mínimos para 100% cobertura de decisiones:

TCInputD1D2D3Return
TC1edad = -5V--“Inválida”
TC2edad = 10FV-“Menor”
TC3edad = 30FFV“Adulto”
TC4edad = 70FFF“Senior”

4 test cases logran 100% cobertura de sentencias y decisiones.

Análisis Avanzado de Cobertura

Vacíos de Cobertura y Qué Significan

Tipo de VacíoSignificadoRiesgo
Sentencia no cubiertaExiste código que ningún test ejecutaCódigo muerto o lógica sin probar
Branch True no cubiertoLa condición nunca fue verdaderaCamino positivo sin probar
Branch False no cubiertoLa condición nunca fue falsaCamino de error sin probar

Limitaciones de Cobertura de Sentencias y Decisiones

Lo que 100% de cobertura NO garantiza:

  1. Código faltante. La cobertura mide lo que existe, no lo que falta.
  2. Defectos dependientes de datos. if (x > 0) con x=1 y x=-1 da 100% pero pierde el defecto de límite si debería ser >=.
  3. Defectos de combinación. Dos decisiones independientes podrían interactuar inesperadamente.
  4. Issues no funcionales. Performance, seguridad y usabilidad son invisibles a la cobertura.

Ejemplo Real: Procesamiento de Pagos

def procesar_pago(monto, metodo, moneda):
    if monto <= 0:                                # D1
        raise ValueError("Monto inválido")

    if metodo == "tarjeta_credito":               # D2
        comision = monto * 0.029
    elif metodo == "transferencia":               # D3
        comision = 1.50
    else:
        raise ValueError("Método no soportado")

    if moneda != "USD":                           # D4
        comision += 0.50

    return monto + comision

Test set mínimo para 100% cobertura de decisiones:

TCmontométodomonedaCubre
TC1-10anyanyD1-V
TC2100tarjeta_creditoUSDD1-F, D2-V, D4-F
TC3100transferenciaEURD1-F, D2-F, D3-V, D4-V
TC4100paypalUSDD1-F, D2-F, D3-F

Herramientas para Medir Cobertura

LenguajeHerramientaComando
Pythoncoverage.pycoverage run -m pytest && coverage report
JavaScriptIstanbul/nycnyc mocha
JavaJaCoCoIntegrado con Maven/Gradle
GoIntegradogo test -cover

Ejercicio: Diseña Tests para Cobertura

Escenario: Analiza esta función y diseña el set mínimo de tests para 100% cobertura de decisiones:

def validar_password(password):
    if len(password) < 8:
        return {"valido": False, "error": "Muy corto"}

    tiene_mayuscula = any(c.isupper() for c in password)
    tiene_digito = any(c.isdigit() for c in password)

    if not tiene_mayuscula:
        return {"valido": False, "error": "Necesita mayúscula"}

    if not tiene_digito:
        return {"valido": False, "error": "Necesita dígito"}

    if len(password) > 64:
        return {"valido": False, "error": "Muy largo"}

    return {"valido": True, "error": None}

Tareas:

  1. Identificar todas las decisiones y sus resultados
  2. Diseñar el set mínimo de tests para 100% cobertura de decisiones
  3. Calcular cobertura de sentencias para tu set
Pista

Hay 4 puntos de decisión (4 sentencias if), cada uno con resultados True y False = 8 resultados totales. Los retornos tempranos significan que necesitas pensar cuáles decisiones son alcanzables.

Solución

Decisiones: D1: len < 8 (V/F), D2: not mayúscula (V/F), D3: not dígito (V/F), D4: len > 64 (V/F)

Set mínimo (5 test cases):

TCInputD1D2D3D4Return
TC1"corto"V---Muy corto
TC2"todominuscula1"FV--Necesita mayúscula
TC3"Sindigitos"FFV-Necesita dígito
TC4"A" + "a"*64 + "1"FFFVMuy largo
TC5"ValidPass1"FFFFVálido

Cobertura de sentencias: 100% Cobertura de decisiones: 100%

Tips de Profesional

  • Usa la cobertura como guía, no como objetivo. 100% cobertura no significa 100% calidad. Pero baja cobertura definitivamente significa baja confianza.
  • Enfócate en cobertura de decisiones sobre sentencias. Es un criterio más fuerte que cuesta poco esfuerzo extra.
  • Investiga branches no cubiertos. A veces es código muerto que debería eliminarse. Otras veces es manejo crítico de errores que necesita tests.
  • Combina con técnicas de caja negra. La cobertura te dice qué código se ejecutó; EP/BVA te dice qué valores probar.
  • Establece objetivos realistas. 80% de cobertura es práctico para la mayoría de proyectos.