Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Testing y Verificación Básica

Pruebas unitarias, aserciones con assert y cobertura de ramas

Universidad Nacional de Río Negro

Prerrequisitos: funciones, condicionales y compilación con GCC.

Objetivos: 1. Formular un caso de prueba con entrada y resultado esperado. 2. Usar assert para verificar una propiedad de una función.

Comprobación de salida: escribí tres aserciones que cubran un caso normal, un borde y una entrada inválida documentada.

Testing y Verificación Básica

Introducción

El testing o prueba de software es una práctica de ingeniería fundamental que consiste en verificar que el código funciona como se espera bajo diferentes condiciones. En programación estructurada, no basta con que el código “parezca” funcionar; es necesario someterlo a pruebas sistemáticas que validen su comportamiento y su estado ante múltiples escenarios de entrada.

¿Para qué probar?

Las pruebas cumplen propósitos didácticos y técnicos fundamentales:


Desarrollo

Anatomía de una Prueba Unitaria (AAA)

Las pruebas unitarias (unit tests) verifican de forma aislada el comportamiento de componentes de software individuales (normalmente funciones específicas).

Una prueba unitaria robusta se estructura sistemáticamente siguiendo el patrón AAA (Arrange-Act-Assert):

  1. Arrange (Preparar): Configura los datos de entrada, las condiciones iniciales y el resultado esperado.

  2. Act (Actuar): Invoca la función bajo prueba con los parámetros preparados y captura el valor de retorno.

  3. Assert (Verificar): Comprueba que el resultado obtenido coincide exactamente con el valor esperado utilizando aserciones.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <assert.h>
// Declaración de la función a probar
int calcular_suma(int a, int b);
void test_suma_valores_positivos(void)
{
    // 1. Arrange: Preparar datos
    int a = 5;
    int b = 3;
    int esperado = 8;
    // 2. Act: Invocar la función
    int resultado = calcular_suma(a, b);
    // 3. Assert: Verificar
    assert(resultado == esperado);
}
Categorías de Casos de Prueba

Al diseñar una suite de pruebas, se deben contemplar obligatoriamente las siguientes categorías de escenarios:

Aserciones con assert y Cobertura de Ramas

La herramienta básica para la verificación estática y dinámica en desarrollo C es la macro assert, declarada en el archivo de cabecera <assert.h>.

Funcionamiento de la macro assert

La macro evalúa una expresión lógica. Si es verdadera, el programa continúa normalmente; si es falsa, la macro aborta inmediatamente la ejecución del programa mostrando por la salida de error estándar (stderr) la expresión lógica fallida, el nombre del archivo fuente y la línea física del código.

1
2
3
4
5
6
7
#include <assert.h>
void procesar_indice(int *arreglo, int idx)
{
    // Aserción de desarrollo para verificar precondiciones físicas
    assert(arreglo != NULL);
    assert(idx >= 0);
}
Cobertura de Ramas (Branch Coverage)

La cobertura de código indica qué caminos lógicos de nuestras funciones fueron ejecutados durante las pruebas. El estándar riguroso para programación estructurada es lograr el 100% de cobertura de ramas (branch coverage), garantizando que se prueben tanto las condiciones verdaderas como las falsas de cada bifurcación del flujo de control.

1
2
3
4
5
6
7
8
9
10
11
int valor_absoluto(int x)
{
    if (x < 0)
    { // Bifurcación en rama 1 y rama 2
        return -x;
    }
    return x;
}
// Para lograr 100% de cobertura de ramas debemos invocar:
assert(valor_absoluto(-5) == 5); // Cubre la rama verdadera (x < 0)
assert(valor_absoluto(5) == 5);  // Cubre la rama falsa (x >= 0)

Ejercicios de Autoevaluación

Anatomía de Pruebas Unitarias

Aserciones y Cobertura de Ramas


Glosario

Prueba unitaria (Unit Test)
Método de verificación que examina de forma aislada el comportamiento de bloques individuales de código fuente (habitualmente funciones).
AAA (Arrange-Act-Assert)
Patrón estructurado para redactar pruebas unitarias en tres pasos: Preparar (Arrange), Actuar (Act) y Verificar (Assert).
Caso límite (Boundary Case)
Caso de prueba diseñado para evaluar valores de entrada que están en los límites o extremos del rango de validez.
Caso de esquina (Corner Case)
Caso de prueba excepcional donde se combinan en simultáneo múltiples condiciones o factores límite del sistema.
Cobertura de ramas (Branch Coverage)
Métrica que mide el porcentaje de bifurcaciones condicionales que han sido evaluadas en caminos verdaderos y falsos durante la ejecución de las pruebas.

Síntesis y Resumen

En esta unidad cubriste los fundamentos prácticos del testeo de software en C:


References y Lecturas Complementarias