Economía del Testing

La Pirámide de Testing: Rápido, Confiable, Barato

Un bug detectado en desarrollo cuesta minutos de corrección. El mismo bug en producción cuesta reputación y miles de dólares. La pirámide define la proporción ideal de pruebas para detectar errores lo antes posible.

Unitarios JUnit 5 + Mocks puros en RAM · milisegundos · 75% Integración Contexto Spring + BD en memoria · segundos · 20% E2E Navegador y red reales · minutos · 5%
Antipatrón: Cono de Helado Invertido

Muchos tests E2E frágiles que tardan horas en correr y suites de integración tan pesadas que nadie las ejecuta localmente. El feedback llega tarde y el equipo deja de confiar en los tests.

Calculadora: El Costo del Feedback Elegí una suite de 500 tests y mirá cuánto tardás en saber si algo se rompió
500 × 5 ms ✓ 2.5 segundos — feedback casi instantáneo
Patrón AAA

Arrange, Act, Assert: La Anatomía de un Test

Cada test responde a una única pregunta y se estructura en 3 fases: preparar el escenario, ejecutar la acción exacta que se quiere probar, y verificar el resultado.

1. Arrange

Se instancia la clase bajo prueba y se preparan los argumentos.

Calculadora calc = new Calculadora();
int a = 5;
int b = 10;
2. Act

Una única llamada: la acción exacta que el test verifica.

int resultado = calc.sumar(a, b);
3. Assert

Se compara el valor esperado contra el valor obtenido.

assertEquals(15, resultado);
esperado: 15 obtenido: 15 ✓ pass
@BeforeEach: Instancia Limpia por Test Corré la suite y mirá cómo cada test arranca sin estado compartido
testSumar() @BeforeEach → new Calculadora() —
testRestar() @BeforeEach → new Calculadora() —
testDividirPorCero() @BeforeEach → new Calculadora() —

Cada instancia es nueva: un test no puede contaminar el estado del siguiente.

Aislamiento con Test Doubles

Mocks con Mockito: Aislar sin Tocar el Mundo Real

Probar PedidoService no debería conectarse a la pasarela de pagos real ni cobrar dinero de verdad. Un mock es un doble controlado que responde exactamente como se le indique.

Mock PasarelaPago
when(pasarela.cobrar(pedido))
  .thenReturn(true);
Clase Real Bajo Prueba PedidoService

Su lógica de negocio se ejecuta tal cual; solo sus colaboradores externos son dobles.

verify(pasarela, times(1)).cobrar(pedido);
Mock InventarioRepository
when(inventario.hayStock(sku))
  .thenReturn(true);
Simulador de Escenarios Cambiá el comportamiento del mock y mirá cómo reacciona el servicio
when(pasarela.cobrar(pedido)).thenReturn(true); ✓ Pedido CONFIRMADO — verify(pasarela, times(1)).cobrar(...) pasa
Arquitectura en Capas

Controller, Service, Repository: Separar Responsabilidades

Un microservicio REST separa estrictamente la traducción HTTP/JSON, las reglas de negocio y la persistencia. La inyección de dependencias por constructor es lo que hace testeable cada capa por separado.

@RestController ProductoController

Recibe @RequestBody ProductoDTO, valida la entrada y devuelve un ResponseEntity.

@Service ProductoService

Aplica reglas de precios, descuentos y validaciones de dominio puras.

@Repository ProductoRepository

Comunica con la base de datos vía SQL / Hibernate / JPA.

Sin Inyección de Dependencias Acoplado al Service real: imposible inyectar un mock
public class ProductoController {
    ProductoService service =
        new ProductoServiceImpl();
}
Con Inyección por Constructor El test pasa un mock, sin levantar el contenedor Spring
public class ProductoController {
    private final ProductoService service;

    ProductoController(ProductoService service) {
        this.service = service;
    }
}
De Extremo a Extremo

El Ciclo de Vida de una Petición REST

Traza completa de un POST /productos con payload JSON, desde el cliente hasta el disco y de vuelta.

POST /productos { "nombre": "Café", "precio": 3.5 }
  1. 1
    Petición HTTP entrante

    El cliente envía el JSON en el cuerpo de la petición POST.

  2. 2
    Jackson → ProductoDTO

    Jackson deserializa el JSON en un objeto Java ProductoDTO.

  3. 3
    Controller → Service

    productoService.crear(dto) : el Controller delega en el Service.

  4. 4
    Reglas de negocio

    El Service calcula impuestos y transforma el DTO en la entidad de dominio Producto.

  5. 5
    Persistencia

    INSERT INTO productos ... : el Repository guarda la entidad en la base relacional.

  6. 6
    HTTP 201 Created

    El Controller empaqueta la respuesta con el header Location del nuevo recurso.