TechLead

La Pirámide de Testing

Comprendiendo el balance óptimo de tests unitarios, de integración y E2E

¿Qué es la Pirámide de Testing?

La Pirámide de Testing es un framework que te ayuda a crear una suite de tests balanceada y eficiente. Sugiere tener muchos tests unitarios rápidos y económicos en la base, menos tests de integración en el medio, y aún menos tests end-to-end lentos y costosos en la cima.

🎯 Principio Central:

Escribe tests al nivel más bajo posible. Los tests unitarios son rápidos y identifican fallas con precisión. Los tests E2E son lentos pero proporcionan la mayor confianza. El balance es clave.

La Pirámide de Testing Tradicional

Tests E2E
5-10%
Tests de Integración
20-30%
Tests Unitarios
60-70%

🟢 Tests Unitarios

  • • Milisegundos para ejecutar
  • • Testean funciones individuales
  • • Fáciles de depurar
  • • Más baratos de mantener

🟡 Tests de Integración

  • • Segundos para ejecutar
  • • Testean interacciones de módulos
  • • Complejidad moderada
  • • Detectan bugs de integración

🔴 Tests E2E

  • • Minutos para ejecutar
  • • Testean flujos completos
  • • Difíciles de depurar
  • • Mayor confianza

¿Por Qué Esta Forma?

Velocidad y Retroalimentación

Los tests unitarios se ejecutan en milisegundos, dándote retroalimentación instantánea. Los tests E2E pueden tomar minutos.

// Comparación de velocidad
Suite de Tests Unitarios:     0.5 segundos  ✓
Tests de Integración:          15 segundos   ⚡
Suite de Tests E2E:            5 minutos     🐌

Desarrollador ejecuta tests: Cada pocos minutos
Pipeline CI/CD:               Cada commit

Tests rápidos = Más testing = Mejor código

Costo y Mantenimiento

Tests Unitarios: Se rompen raramente, fáciles de arreglar cuando lo hacen

Tests de Integración: Pueden ser inestables, requieren más configuración

Tests E2E: Más frágiles, se rompen con cambios de UI, timeouts, etc.

Depuración y Precisión

Cuando un test unitario falla, sabes exactamente qué función está rota. Cuando un test E2E falla, el bug podría estar en cualquier parte del stack.

Pirámide de Testing en la Práctica

// Ejemplo: Funcionalidad de Carrito de Compras E-commerce

// ✅ TESTS UNITARIOS (Muchos) - Testear funciones puras
// cart.js
export function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

export function applyDiscount(total, discountPercent) {
  return total * (1 - discountPercent / 100);
}

// cart.test.js
describe('Cálculos del carrito', () => {
  test('calcula el total correctamente', () => {
    const items = [
      { price: 10, quantity: 2 },
      { price: 5, quantity: 3 }
    ];
    expect(calculateTotal(items)).toBe(35);
  });

  test('aplica descuento correctamente', () => {
    expect(applyDiscount(100, 10)).toBe(90);
    expect(applyDiscount(100, 0)).toBe(100);
  });

  test('maneja carrito vacío', () => {
    expect(calculateTotal([])).toBe(0);
  });
});

// ⚡ TESTS DE INTEGRACIÓN (Moderados) - Testear componentes trabajando juntos
// CartComponent.test.jsx
describe('Integración Componente Carrito', () => {
  test('actualiza total cuando se agregan items', () => {
    render();
    
    // Agregar items
    fireEvent.click(screen.getByText('Agregar Item 1'));
    fireEvent.click(screen.getByText('Agregar Item 2'));
    
    // Verificar que el total se actualizó
    expect(screen.getByText(/Total: $35/)).toBeInTheDocument();
  });

  test('aplica código de descuento', async () => {
    render();
    
    // Ingresar código de descuento
    fireEvent.change(screen.getByPlaceholderText('Código de descuento'), {
      target: { value: 'SAVE10' }
    });
    fireEvent.click(screen.getByText('Aplicar'));
    
    // Verificar que se aplicó el descuento
    await waitFor(() => {
      expect(screen.getByText(/Descuento: 10%/)).toBeInTheDocument();
    });
  });
});

// 🌐 TESTS E2E (Pocos) - Testear flujos críticos de usuario
// checkout.e2e.test.js
describe('Flujo de Checkout E2E', () => {
  test('usuario puede completar compra', async () => {
    // Navegar a la tienda
    await page.goto('http://localhost:3000');
    
    // Agregar items al carrito
    await page.click('[data-testid="add-product-1"]');
    await page.click('[data-testid="add-product-2"]');
    
    // Ir a checkout
    await page.click('[data-testid="cart-button"]');
    await page.click('[data-testid="checkout-button"]');
    
    // Llenar información de envío
    await page.fill('[name="email"]', 'user@example.com');
    await page.fill('[name="address"]', '123 Main St');
    
    // Enviar pago
    await page.fill('[name="cardNumber"]', '4242424242424242');
    await page.click('[data-testid="submit-payment"]');
    
    // Verificar éxito
    await expect(page.locator('text=Orden confirmada')).toBeVisible();
  });
});

📊 Distribución de Cobertura:

  • Tests Unitarios: 20 tests cubriendo toda la lógica de cálculo (se ejecutan en 0.1s)
  • Tests de Integración: 8 tests para interacciones de componentes (se ejecutan en 5s)
  • Tests E2E: 3 tests solo para rutas críticas (se ejecutan en 2 min)

El Trofeo de Testing (Alternativa Moderna)

E2E
10%
Tests de Integración
50%
Tests Unitarios
30%
Estático
10%

🏆 ¿Por Qué el Trofeo?

  • Tests de integración proporcionan el mejor ROI para apps frontend
  • • Testean componentes como los usuarios interactúan con ellos
  • • Menos mocking = tests más realistas
  • • Aún lo suficientemente rápidos para ejecutar frecuentemente

Qué Testear en Cada Nivel

Tests Unitarios

  • ✓ Funciones puras
  • ✓ Lógica de negocio
  • ✓ Utilidades y helpers
  • ✓ Casos extremos
  • ✓ Transformaciones de datos
  • ✓ Validaciones

Tests de Integración

  • ✓ Interacciones de componentes
  • ✓ Integración de API
  • ✓ Consultas de base de datos
  • ✓ Envíos de formularios
  • ✓ Enrutamiento
  • ✓ Gestión de estado

Tests E2E

  • ✓ Rutas críticas de usuario
  • ✓ Flujos de autenticación
  • ✓ Procesamiento de pagos
  • ✓ Flujos multi-página
  • ✓ Solo happy paths
  • ✓ Funcionalidades críticas de ingresos

Anti-Patrón: El Cono de Helado

❌ Qué Evitar

Unitarios
10%
Integración
20%
Tests E2E
70%

Problemas:

  • • Suite de tests lenta (los desarrolladores no la ejecutarán)
  • • Tests inestables que fallan aleatoriamente
  • • Difícil de depurar fallas
  • • Costoso de mantener
  • • Mala experiencia de desarrollador

Construyendo Tu Estrategia de Testing

// Enfoque paso a paso para testear una nueva funcionalidad

// 1. Comienza con tests unitarios para lógica de negocio
describe('OrderProcessor', () => {
  test('calcula total de orden con impuesto', () => {
    const processor = new OrderProcessor({ taxRate: 0.08 });
    const order = { subtotal: 100 };
    expect(processor.calculateTotal(order)).toBe(108);
  });
});

// 2. Agrega tests de integración para comportamiento de componentes
describe('CheckoutForm', () => {
  test('envía orden cuando el formulario es válido', async () => {
    const onSubmit = jest.fn();
    render();
    
    // Llenar formulario...
    fireEvent.click(screen.getByText('Realizar Orden'));
    
    await waitFor(() => {
      expect(onSubmit).toHaveBeenCalledWith(expect.objectContaining({
        email: 'test@example.com',
        total: 108
      }));
    });
  });
});

// 3. Agrega UN test E2E para la ruta crítica
test('usuario puede completar checkout', async () => {
  // Flujo completo de usuario desde carrito hasta confirmación
  await page.goto('/cart');
  await page.click('text=Checkout');
  // ... llenar todos los formularios ...
  await page.click('text=Realizar Orden');
  await expect(page.locator('text=Gracias')).toBeVisible();
});

💡 Puntos Clave

  • Escribe más tests unitarios que tests de integración, más tests de integración que E2E
  • Tests rápidos se ejecutan más a menudo, proporcionando mejor retroalimentación
  • Tests E2E deberían cubrir solo flujos críticos de usuario
  • Tests de integración proporcionan el mejor balance para apps frontend
  • Ajusta la pirámide basándote en tu tipo de aplicación
  • Evita el cono de helado - demasiados tests E2E lentos

📚 Más Temas de Testing

Explora los 6 temas de testing para construir una comprensión completa del testing de software.

Ver Todos los Temas