Regla 0x8002h: Escribí al menos una prueba por cláusula del contrato
Verificacion, testing y depuracion (0x80XX)
0x8002h: Escribí al menos una prueba por cláusula del contrato¶
Enunciado normativo¶
Cada cláusula del contrato de una función (cada precondición, cada postcondición y cada caso de error documentado) DEBE tener al menos una prueba que la verifique. NO DEBE considerarse terminada una función sin pruebas que cubran su especificación completa.
¿Por qué existe esta regla?¶
El problema¶
Una prueba que sólo verifica “el caso feliz” no demuestra que la función cumpla su contrato: deja sin cubrir justamente las cláusulas que describen errores y bordes. Como el contrato enumera explícitamente lo que la función promete, cada promesa es un candidato directo a prueba. Esto convierte el testing en una tarea mecánica y completa, no en una inspiración.
Además, las pruebas sirven de documentación ejecutable: muestran a un lector futuro cómo se usa la función y qué se espera ante cada entrada.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Errores no detectados | El camino de error nunca se ejecuta hasta producción. |
| Regresiones | Un cambio rompe una cláusula sin que nadie lo note. |
| Falsa cobertura | Muchas pruebas del mismo caso y ninguna del borde. |
| Diseño | Un contrato que no se puede probar suele estar mal especificado. |
Fundamento en la cátedra¶
Extiende 0x8001h: Una aserción por cada función de prueba (una aserción por prueba) del “cómo” al “qué”:
no basta con escribir asserts, hay que asegurarse de cubrir cada cláusula
del contrato. Se apoya en 0x2016h: Escribí el contrato de la función antes de implementarla (contrato) y 0x7006h: Probá explícitamente los casos borde
(casos borde).
Alcance y excepciones¶
Aplica a funciones con contrato explícito. Las funciones triviales (getters) pueden cubrirse con una prueba general. La obligación es proporcional a la cantidad de cláusulas: a más promesas, más pruebas.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Sólo el caso feliz¶
Contrato: promedio(v, n) devuelve el promedio, y 0.0 si n == 0.
assert(promedio((int[]){2, 4, 6}, 3) == 4.0);
/* fin de las pruebas */Por qué falla: la cláusula “si n == 0 devuelve 0.0” no se prueba; podría
estar mal implementada y nadie lo sabría.
❌ Contraejemplo 2 — Muchas pruebas redundantes¶
assert(promedio((int[]){1, 2}, 2) == 1.5);
assert(promedio((int[]){2, 4}, 2) == 3.0);
assert(promedio((int[]){3, 6}, 2) == 4.5);Por qué falla: tres variantes del mismo caso (dos elementos). Cubren lo mismo y dejan sin probar el borde.
✅ Ejemplo conforme 1 — Una prueba por cláusula¶
/* Clausula: caso comun. */
assert(promedio((int[]){2, 4, 6}, 3) == 4.0);
/* Clausula: arreglo vacio devuelve 0.0. */
assert(promedio(NULL, 0) == 0.0);
/* Clausula: un elemento. */
assert(promedio((int[]){7}, 1) == 7.0);Cada assert corresponde a una promesa distinta del contrato.
✅ Ejemplo conforme 2 — Cobertura de errores¶
Contrato: pila_apilar devuelve PILA_LLENA si no hay capacidad.
pila_t *p = pila_crear(1);
assert(pila_apilar(p, 10) == PILA_OK);
assert(pila_apilar(p, 20) == PILA_LLENA);
pila_destruir(p);El caso de error también es una cláusula del contrato y se prueba.
⚠️ Casos límite¶
Funciones sin retorno (
void): probar por efecto observable (parámetro de salida, archivo, etc.).Contratos con
NULL: probar explícitamente siNULLes válido o no.Punto flotante: comparar con tolerancia, no con
==exacto (relacionado con0x301Fh).Efectos de borde del sistema: agotar la memoria no es práctico de probar; se documenta como no cubierto.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
assert | gcc archivo.c -o test && ./test | Cada cláusula tiene su assert. |
gcov | gcc --coverage + gcov | Líneas de la función no ejecutadas por las pruebas. |
dredd | reporte de cobertura | Cláusulas del contrato sin prueba. |
Checklist de autocontrol¶
¿Cada precondición tiene una prueba?
¿Cada postcondición tiene una prueba?
¿Cada caso de error documentado tiene una prueba?
¿Evité probar el mismo caso varias veces?
Reglas relacionadas¶
0x2016h: Escribí el contrato de la función antes de implementarla — contrato de la función.
0x8001h: Una aserción por cada función de prueba — una aserción por función de prueba.
0x7006h: Probá explícitamente los casos borde — casos borde obligatorios.
0x8003h: Verificá con gcc, gdb y valgrind antes de entregar — verificación con herramientas antes de entregar.