0x8001h: Una aserción por cada función de prueba¶
Enunciado normativo¶
DEBE verificarse un único hecho por función de prueba. Cuando haga falta probar varios casos, se escribe una función de prueba por caso, o bien una función parametrizada que reciba los argumentos y el resultado esperado y contenga una sola aserción.
¿Por qué existe esta regla?¶
El problema¶
Cuando una función de prueba encadena varias aserciones, la primera que falla aborta el resto y el reporte no aclara cuál de los casos se rompió: los posteriores nunca se ejecutan. Con una aserción por prueba, el nombre de la función es el diagnóstico y agregar un caso no altera a los anteriores.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Diagnóstico pobre | Un fallo esconde cuántos casos más estaban mal. |
| Falsa sensación | La prueba “pasa” hasta la línea que aborta; el resto nunca corrió. |
| Aislamiento | No se puede atribuir la falla a un caso concreto. |
Fundamento en el estándar y en la cátedra¶
La macro assert se define en <assert.h> (§7.2): si la expresión es falsa,
escribe el diagnóstico y llama a abort (§7.2.1.1), por lo que no retorna.
Por eso una segunda aserción en la misma función nunca se evalúa.
Alcance y excepciones¶
Aplica a las funciones de prueba de la entrega y a los ejemplos de assert que
acompañan cada ejercicio. No obliga a una función por cada valor posible: se
eligen los casos representativos (típico, borde, inválido). La variante
parametrizada permite cubrir una tabla de casos con una sola aserción.
Excepción razonable: las pruebas de propiedades que requieren preparar un estado (abrir un archivo, reservar memoria) pueden tener código de arrange/act previo; lo que se limita a una es la aserción final sobre el hecho.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Tres hechos en una función¶
void test_calculadora(void)
{
assert(sumar(2, 2) == 4);
assert(restar(4, 2) == 2);
assert(multiplicar(2, 3) == 6);
}Por qué falla: si sumar está mal, abort termina la función y nunca se sabe
si restar y multiplicar funcionan. El nombre “calculadora” no identifica el
caso roto.
❌ Contraejemplo 2 — Aserción que depende del estado previo¶
void test_pila(void)
{
struct pila_t *p = pila_crear();
assert(p != NULL);
pila_apilar(p, 10);
assert(pila_desapilar(p) == 10);
pila_apilar(p, 20);
assert(pila_desapilar(p) == 20);
}Por qué falla: las aserciones verifican hechos distintos (creación, primer apilar, segundo apilar) encadenados. Si falla el primero, no se sabe nada de los otros; y el nombre no indica qué hecho se está midiendo.
✅ Ejemplo conforme 1 — Una función, un hecho¶
void test_suma_positivos(void)
{
assert(sumar(2, 2) == 4);
}
void test_suma_opuestos(void)
{
assert(sumar(5, -3) == 2);
}Cada nombre declara el caso. Un fallo identifica de inmediato el escenario y no arrastra a los demás.
✅ Ejemplo conforme 2 — Prueba parametrizada con una sola aserción¶
static void verificar_suma(int a, int b, int esperado)
{
assert(sumar(a, b) == esperado);
}
void test_suma_casos(void)
{
verificar_suma(2, 2, 4);
verificar_suma(5, -3, 2);
verificar_suma(INT_MAX, 0, INT_MAX);
}La función de prueba contiene varias invocaciones, pero cada una ejecuta su
propia aserción con su propio reporte. El caso INT_MAX documenta el borde del
dominio y conecta con 0x2011h: Prohibición de asignaciones múltiples a una variable sin lectura intermedia (dead store) (dead stores).
⚠️ Casos límite¶
Valores extremos:
INT_MIN,INT_MAX,0, cadena vacía yNULLson casos obligatorios cuando el dominio los admite.NDEBUG: con esa macro definida,assertse elimina; las pruebas se compilan sin-DNDEBUG.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Más de un assert en el cuerpo de una función de prueba. |
gcc / clang | gcc -Wall -Wextra -std=c11 ... | assert dentro del mismo bloque sin retorno intermedio. |
grep | grep -c assert pruebas.c | Conteo de aserciones por función. |
Checklist de autocontrol¶
¿Cada función de prueba verifica un solo hecho?
¿El nombre de la prueba identifica el caso?
¿Incluí al menos un caso borde (
0,INT_MAX,NULL)?¿Una falla deja intactas las demás pruebas?
Reglas relacionadas¶
0x2008h: Los ejercicios deben ser resueltos mediante funciones — la solución vive en funciones, y cada una recibe su prueba.
0x2002h: Las funciones no deben contener printf o scanf, a menos que ese sea su propósito explícito — sin E/S embebida, el
assertes suficiente y limpio.0x2007h: Los valores de retorno numéricos deben definirse como constantes de preprocesador o enums — los códigos de error se comparan con nombres simbólicos.
0x3008h: Los punteros nulos deben ser inicializados y comparados con NULL, no con 0 — la comparación con
NULLusaNULL, no0.