Más allá de la cobertura de decisiones

En la lección anterior, aprendiste sobre cobertura de sentencias y decisiones. La cobertura de decisiones asegura que cada branch se ejecute, pero no te dice si las condiciones individuales dentro de una decisión compuesta están realmente probadas. Considera este código:

if sensor_active and temperature > threshold and not emergency_override:
    activate_cooling_system()

La cobertura de decisiones solo requiere dos test cases — uno donde toda la expresión sea True y otro donde sea False. Pero ¿cuál condición causó el resultado False? La cobertura de decisiones no se preocupa por eso. Para un sistema de enfriamiento en una planta nuclear, esa distinción es crítica.

Aquí es donde entran los criterios de cobertura a nivel de condición, culminando en MC/DC — la medida de cobertura práctica más rigurosa usada en industrias safety-critical.

Cobertura de condiciones (Condition Coverage)

La cobertura de condiciones requiere que cada condición individual en una decisión tome ambos valores True y False a lo largo del test suite.

Para la decisión A AND B:

TestABDecisión
1TFF
2FTF

Esto logra 100% de cobertura de condiciones — tanto A como B toman valores True y False. Pero observa: el resultado de la decisión es False en ambos casos. Nunca probamos el branch True. La cobertura de condiciones por sí sola puede omitir branches completos.

Cobertura Branch/Condition

La cobertura branch/condition combina ambos requisitos: cada condición debe tomar ambos valores Y cada branch debe ejecutarse.

Para A AND B:

TestABDecisión
1TTT
2FFF

Ahora cubrimos ambos branches y ambas condiciones toman True y False. Pero todavía hay una brecha — no podemos determinar si A afecta independientemente el resultado, porque A y B siempre cambian juntas.

Cobertura de condiciones múltiples

La cobertura de condiciones múltiples (MCC) requiere probar cada combinación posible de valores de condiciones. Para A AND B, eso significa:

TestABDecisión
1TTT
2TFF
3FTF
4FFF

Esto es exhaustivo pero impráctico para código real. Con N condiciones, necesitas 2^N test cases. Una decisión con 10 condiciones requiere 1,024 tests. Para 20 condiciones: más de un millón.

Modified Condition/Decision Coverage (MC/DC)

MC/DC es el punto intermedio práctico. Fue desarrollado por NASA y Boeing en los años 90 para software de aviónica y se convirtió en el criterio de cobertura requerido para software de aviación safety-critical bajo DO-178B (ahora DO-178C).

MC/DC requiere:

  1. Cada punto de entrada y salida se invoca (cobertura de bloques básicos)
  2. Cada condición toma ambos valores True y False (cobertura de condiciones)
  3. Cada decisión toma ambos valores True y False (cobertura de decisiones)
  4. Cada condición afecta independientemente el resultado de la decisión — para cada condición, existe un par de test cases donde solo esa condición cambia de valor y el resultado de la decisión cambia

La regla 4 es lo que hace especial a MC/DC. Demuestra que cada condición realmente importa.

Derivando test cases para MC/DC

Para la decisión A AND B AND C, sigue este proceso sistemático:

Paso 1: Construye la tabla de verdad completa.

#ABCResultado
1TTTT
2TTFF
3TFTF
4TFFF
5FTTF
6FTFF
7FTFF
8FFFF

Paso 2: Encuentra pares de independencia para cada condición. Un par de independencia para la condición X son dos filas donde X cambia, todas las demás condiciones permanecen iguales, y el resultado de la decisión cambia.

  • Condición A: Filas 1 (T,T,T→T) y 5 (F,T,T→F) — A cambia, B y C se mantienen en T, resultado cambia
  • Condición B: Filas 1 (T,T,T→T) y 3 (T,F,T→F) — B cambia, A y C se mantienen en T, resultado cambia
  • Condición C: Filas 1 (T,T,T→T) y 2 (T,T,F→F) — C cambia, A y B se mantienen en T, resultado cambia

Paso 3: Selecciona el conjunto mínimo cubriendo todos los pares: filas {1, 2, 3, 5} = 4 test cases.

Para N condiciones con AND: N+1 test cases. Para N condiciones con OR: también N+1. Esto es dramáticamente mejor que los 2^N requeridos por cobertura de condiciones múltiples.

MC/DC para operadores mixtos

Cuando una decisión mezcla AND y OR, el proceso es el mismo pero encontrar pares de independencia requiere más cuidado.

Para (A OR B) AND C:

#ABCA OR BResultado
1TTTTT
2TTFTF
3TFTTT
4TFFTF
5FTTTT
6FTFTF
7FFTFF
8FFFFF

Pares de independencia:

  • A: Filas 3 (T,F,T→T) y 7 (F,F,T→F)
  • B: Filas 5 (F,T,T→T) y 7 (F,F,T→F)
  • C: Filas 1 (T,T,T→T) y 2 (T,T,F→F)

Conjunto mínimo: {1, 2, 3, 5, 7} = 5 test cases. Operadores mixtos pueden requerir ligeramente más que N+1.

Dónde se requiere MC/DC

MC/DC no es solo un ejercicio académico. Es mandatorio según estándares reales:

EstándarDominioMC/DC requerido para
DO-178CAviaciónSoftware Level A (falla catastrófica)
ISO 26262AutomotrizSoftware ASIL D (riesgo más alto)
IEC 61508IndustrialSIL 4 (mayor integridad de seguridad)
EN 50128FerroviarioSoftware SIL 3 y SIL 4

Si trabajas en cualquiera de estas industrias, MC/DC no es opcional — es un requisito regulatorio.

Herramientas para análisis MC/DC

Varias herramientas soportan medición MC/DC:

  • VectorCAST — Estándar de la industria para sistemas embedded y safety-critical
  • LDRA — Ampliamente usado en aeroespacial y automotriz
  • Parasoft C/C++test — Soporta MC/DC para codebases C y C++
  • gcov + análisis custom — Opción open-source para tracking básico de condiciones
  • JaCoCo — Herramienta de cobertura Java que soporta cobertura de branches (no MC/DC completo, pero útil como aproximación)

Ejercicio: Derivar test cases MC/DC

Problema 1

Dada la función:

def should_trigger_alert(pressure_high, temp_critical, manual_override):
    return (pressure_high or temp_critical) and not manual_override

Deriva el conjunto mínimo de tests para MC/DC.

Solución

Construye la tabla de verdad para (A OR B) AND NOT C donde A=pressure_high, B=temp_critical, C=manual_override:

#ABCNOT CA OR BResultado
1TTFTTT
2TFFTTT
3FTFTTT
4FFFTFF
5TTTFTF
6TFTFTF
7FTTFTF
8FFTFFF

Pares de independencia:

  • A (pressure_high): Filas 2 (T,F,F→T) y 4 (F,F,F→F)
  • B (temp_critical): Filas 3 (F,T,F→T) y 4 (F,F,F→F)
  • C (manual_override): Filas 2 (T,F,F→T) y 6 (T,F,T→F)

Conjunto mínimo: {2, 3, 4, 6} = 4 test cases.

Testpressure_hightemp_criticalmanual_overrideEsperado
1TrueFalseFalseTrue
2FalseTrueFalseTrue
3FalseFalseFalseFalse
4TrueFalseTrueFalse

Problema 2

Un sistema de control de vuelo tiene esta lógica:

if (altitude_low && airspeed_low && !landing_gear_deployed) {
    trigger_terrain_warning();
}
  1. ¿Cuántos test cases requiere MC/DC?
  2. ¿Cuántos requiere cobertura de condiciones múltiples?
  3. Deriva el conjunto de tests MC/DC.
Solución

Decisión: A AND B AND NOT C donde A=altitude_low, B=airspeed_low, C=landing_gear_deployed.

  1. MC/DC requiere 4 test cases (N+1 = 3+1, ya que NOT C sigue siendo una condición)
  2. Cobertura de condiciones múltiples requiere 8 test cases (2^3)

Tabla de verdad:

#ABCNOT CResultado
1TTFTT
2TTTFF
3TFFTF
4FTFTF

Pares de independencia:

  • A: Filas 1 y 4 (A cambia T→F, B=T, C=F fijos, resultado T→F)
  • B: Filas 1 y 3 (B cambia T→F, A=T, C=F fijos, resultado T→F)
  • C: Filas 1 y 2 (C cambia F→T, A=T, B=T fijos, resultado T→F)

Conjunto mínimo MC/DC: {1, 2, 3, 4} — exactamente 4 tests en lugar de 8.

Tips prácticos para MC/DC

Simplifica antes de analizar. Si una condición contiene sub-expresiones redundantes, simplifica usando álgebra booleana primero. MC/DC en una expresión simplificada requiere menos tests.

Condiciones acopladas vs. desacopladas. Cuando dos condiciones comparten una variable (por ejemplo, x > 5 AND x < 10), no puedes variar una independientemente de la otra. Esto se llama condición acoplada. Las condiciones acopladas pueden requerir pares de independencia diferentes a las desacopladas.

Usa masking MC/DC para expresiones complejas. En masking MC/DC (permitido por DO-178C), se puede demostrar que una condición afecta independientemente la decisión incluso si otras condiciones cambian, siempre que los cambios estén “enmascarados” (no afecten el resultado a través de la estructura lógica). Esto puede reducir aún más el conjunto de tests requerido.

La evaluación de cortocircuito importa. En lenguajes con evaluación de cortocircuito (la mayoría de los lenguajes modernos), A AND B no evalúa B si A es False. Esto significa que algunos test cases MC/DC pueden no ejercitar ciertas condiciones en runtime. Considera si tu herramienta de cobertura tiene en cuenta el comportamiento de cortocircuito.

Puntos clave

  • La cobertura de condiciones prueba condiciones individuales pero puede omitir branches completos
  • La cobertura branch/condition combina ambas pero no demuestra independencia
  • MC/DC demuestra que cada condición afecta independientemente el resultado de la decisión
  • MC/DC requiere N+1 test cases (vs. 2^N para cobertura de condiciones múltiples) — práctico incluso para decisiones complejas
  • MC/DC es mandatorio por DO-178C, ISO 26262 e IEC 61508 para software safety-critical
  • Siempre encuentra pares de independencia sistemáticamente usando tablas de verdad
  • Considera condiciones acopladas, evaluación de cortocircuito y masking MC/DC para decisiones complejas del mundo real