Leccion 68 de 75 10 min de lectura

Introduccion a Testing

Los tests automatizados verifican que tu codigo funciona correctamente y te alertan cuando un cambio rompe algo. Escribir tests es una habilidad fundamental para cualquier desarrollador profesional.

¿Por qué escribir tests?

Probar codigo manualmente es lento y propenso a errores. Los tests automatizados se ejecutan en segundos y verifican todo el sistema cada vez que haces un cambio.

Beneficios del testing

  • Detectar bugs temprano: Encuentras errores antes de que lleguen a produccion
  • Refactorizar con confianza: Puedes cambiar el codigo sabiendo que los tests te avisaran si rompes algo
  • Documentacion viva: Los tests muestran como usar el codigo
  • Mejor diseño: El código fácil de testear suele estar mejor estructurado

Tipos de tests

Existen diferentes niveles de testing, cada uno con su proposito. Los mas comunes son:

Tests unitarios

Verifican una unidad aislada de codigo (funcion, metodo o clase). Son rapidos y especificos.

PHP
<?php

declare(strict_types=1);

// Codigo a testear
class Calculadora
{
    public function sumar(int $a, int $b): int
    {
        return $a + $b;
    }
}

// Test unitario (pseudocodigo conceptual)
$calc = new Calculadora();
$resultado = $calc->sumar(2, 3);

if ($resultado === 5) {
    echo "Test PASADO: sumar funciona correctamente\n";
} else {
    echo "Test FALLIDO: esperaba 5, obtuve $resultado\n";
}

Tests de integracion

Verifican que varios componentes funcionen juntos correctamente. Por ejemplo, que tu codigo interactue bien con la base de datos.

Tests funcionales (end-to-end)

Prueban el sistema completo desde la perspectiva del usuario. Simulan interacciones reales como enviar formularios o navegar paginas.

Piramide de testing

La mayoria de tus tests deberian ser unitarios (base de la piramide), menos tests de integracion (medio), y pocos tests end-to-end (cima).

Estructura de un test

Un test sigue el patron AAA: Arrange, Act, Assert (Preparar, Actuar, Afirmar).

PHP
<?php

declare(strict_types=1);

class Carrito
{
    private array $productos = [];

    public function agregar(string $producto, float $precio): void
    {
        $this->productos[] = ['nombre' => $producto, 'precio' => $precio];
    }

    public function total(): float
    {
        return array_sum(array_column($this->productos, 'precio'));
    }
}

// Test siguiendo AAA
function testCarritoCalculaTotal(): void
{
    // Arrange (Preparar): crear objetos y datos necesarios
    $carrito = new Carrito();

    // Act (Actuar): ejecutar la accion a probar
    $carrito->agregar('Libro', 25.00);
    $carrito->agregar('Cuaderno', 5.50);
    $total = $carrito->total();

    // Assert (Afirmar): verificar el resultado esperado
    if ($total !== 30.50) {
        throw new Exception("Test fallido: esperaba 30.50, obtuve $total");
    }

    echo "Test PASADO: carrito calcula total correctamente\n";
}

testCarritoCalculaTotal();

Caracteristicas de buenos tests

Independientes

Cada test debe poder ejecutarse solo, sin depender de otros tests. El orden de ejecucion no debe afectar el resultado.

Repetibles

Un test debe dar el mismo resultado siempre que se ejecute. Evita dependencias de fecha actual, numeros aleatorios, o estado externo.

Claros

El nombre del test debe describir que se esta probando y cual es el resultado esperado.

PHP
<?php

declare(strict_types=1);

// MAL: nombre poco descriptivo
function test1(): void { /* ... */ }

// BIEN: describe que se prueba y el resultado esperado
function testUsuarioConEmailInvalidoLanzaExcepcion(): void { /* ... */ }
function testCarritoVacioDevuelveTotalCero(): void { /* ... */ }
function testLoginConCredencialesCorrectasDevuelveUsuario(): void { /* ... */ }

Test-Driven Development (TDD)

TDD es una metodologia donde escribes el test antes del codigo. El ciclo es: Rojo - Verde - Refactor.

  1. Rojo: Escribir un test que falle
  2. Verde: Escribir el codigo minimo para que pase
  3. Refactor: Mejorar el codigo sin romper el test
PHP
<?php

declare(strict_types=1);

// Paso 1 (ROJO): Escribir el test primero
function testPalabraEsPalindromo(): void
{
    // Este test falla porque la funcion no existe todavia
    assert(esPalindromo('ana') === true);
    assert(esPalindromo('hola') === false);
}

// Paso 2 (VERDE): Implementar el codigo minimo
function esPalindromo(string $palabra): bool
{
    $palabra = strtolower($palabra);
    return $palabra === strrev($palabra);
}

// Paso 3 (REFACTOR): Mejorar si es necesario
// En este caso el codigo ya es simple y claro

testPalabraEsPalindromo();
echo "Test PASADO\n";

Ejercicios

Ejercicio 1: Identificar componentes del test

Dado el siguiente test, identifica las partes Arrange, Act y Assert:

PHP
<?php

declare(strict_types=1);

function testDescuentoAplicadoCorrectamente(): void
{
    $precio = 100.0;
    $descuento = 0.20;

    $precioFinal = aplicarDescuento($precio, $descuento);

    assert($precioFinal === 80.0);
}
Ver solucion
PHP
<?php

declare(strict_types=1);

function testDescuentoAplicadoCorrectamente(): void
{
    // ARRANGE: preparar los datos necesarios
    $precio = 100.0;
    $descuento = 0.20;

    // ACT: ejecutar la accion a probar
    $precioFinal = aplicarDescuento($precio, $descuento);

    // ASSERT: verificar el resultado
    assert($precioFinal === 80.0);
}

Ejercicio 2: Escribir un test simple

Escribe un test para la siguiente funcion que valida emails:

PHP
<?php

declare(strict_types=1);

function esEmailValido(string $email): bool
{
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
Ver solucion
PHP
<?php

declare(strict_types=1);

function esEmailValido(string $email): bool
{
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

function testEmailValidoDevuelveTrue(): void
{
    // Arrange
    $email = 'usuario@ejemplo.com';

    // Act
    $resultado = esEmailValido($email);

    // Assert
    assert($resultado === true, 'Email valido deberia devolver true');
    echo "Test email valido: PASADO\n";
}

function testEmailInvalidoDevuelveFalse(): void
{
    // Arrange
    $email = 'no-es-un-email';

    // Act
    $resultado = esEmailValido($email);

    // Assert
    assert($resultado === false, 'Email invalido deberia devolver false');
    echo "Test email invalido: PASADO\n";
}

testEmailValidoDevuelveTrue();
testEmailInvalidoDevuelveFalse();

Ejercicio 3: Nombrar tests correctamente

Mejora los nombres de estos tests para que sean mas descriptivos:

PHP
<?php

declare(strict_types=1);

function test1(): void { /* prueba dividir entre cero */ }
function test2(): void { /* prueba que password vacio es invalido */ }
function test3(): void { /* prueba que array vacio devuelve null */ }
Ver solucion
PHP
<?php

declare(strict_types=1);

// Nombres descriptivos que explican que se prueba y el resultado esperado
function testDividirEntreCeroLanzaExcepcion(): void { /* ... */ }
function testPasswordVacioEsInvalido(): void { /* ... */ }
function testBuscarEnArrayVacioDevuelveNull(): void { /* ... */ }

Te está gustando el curso?

Tenemos cursos premium con proyectos reales y soporte personalizado.

Descubrir cursos premium