# Testing — Fundamental Skill

Skill para escribir y mantener tests de calidad en ventures de Fundamental. Usar al escribir código nuevo, refactorizar, o configurar CI.

---

## Tipos de test — Pirámide

### Unitarios
- Scope: Una función o método
- Velocidad: milisegundos
- Confianza: Baja — prueba lógica aislada
- La base. Cada función pública debe tener al menos un test unitario.

### Integración
- Scope: Dos o más módulos juntos
- Velocidad: ms a segundos
- Confianza: Media — prueba que las piezas encajan
- Verifican código con dependencias reales: DB, API, filesystem. Sin mocks.

### End-to-end
- Scope: Sistema completo
- Velocidad: segundos a minutos
- Confianza: Alta — prueba lo que el usuario ve
- Flujos críticos: login, checkout, onboarding. 5 buenos E2E > 50 frágiles.

### Snapshot
- Scope: Output de un componente
- Velocidad: milisegundos
- Confianza: Baja — detecta cambios no intencionales
- Útiles para UI components y serializers.

### Pirámide
- E2E: Pocos (flujos críticos de negocio)
- Integración: Algunos (contratos entre módulos)
- Unitarios: Muchos (lógica de negocio pura, sin IO)

---

## TDD — Rojo → Verde → Refactor

1. **🔴 Rojo**: Escribí un test que falle. Solo uno. El más chico que pruebe algo útil.
2. **🟢 Verde**: Escribí el mínimo código para que pase. No refactores, no optimices.
3. **🔵 Refactor**: Limpiá, extraé métodos, mejorá nombres. Los tests dan la red de seguridad.

### Reglas
- Nunca escribas código de producción sin un test que falle primero.
- El ciclo debe durar minutos, no horas.
- TDD es diseño, no solo testing. Te obliga a pensar en la interfaz antes que en la implementación.
- Si testear algo es difícil, tu diseño está mal. Acoplamiento alto = tests imposibles.

---

## Buenas prácticas

### Nombres que explican el escenario
- ✅ should_return_422_when_email_is_missing()
- ❌ test_validation_1()

### Estructura AAA: Arrange, Act, Assert
```js
// Arrange
const user = buildUser()
// Act
const result = validate(user)
// Assert
expect(result.error).toBe("email required")
```

### Un concepto por test
- ✅ test("retorna 422 si falta email") / test("retorna 422 si email es inválido")
- ❌ test("validación de email funciona")

### Testeá comportamiento, no implementación
- ✅ expect(service.getTotal(cart)).toBe(150)
- ❌ expect(service.calculateTax).toHaveBeenCalledTimes(1)

### Fixtures compartidas, no herencia
- ✅ beforeEach(() => { user = await createTestUser() })
- ❌ class UserTest extends BaseTest { ... }

### Mocks solo para IO externa
- ✅ mockEmailService.send = jest.fn()
- ❌ mockUserRepository.save = jest.fn() // esto es integración, no unit

---

## Anti-patrones

- ❌ Test que pasa siempre (false positive): forzalo a fallar primero
- ❌ Test flaky: causa #1 = dependencia de tiempo/orden/red. Usá fake timers y seeds determinísticas
- ❌ Test que testea el mock, no el código: si mockeás el 80%, no testeás integración real
- ❌ Cobertura 100% como meta: cuesta el doble que 80% sin agregar el doble de seguridad

---

## CI & Coverage

### Gates en orden
1. Lint (ESLint/Biome) — < 30 segundos
2. Type check (TypeScript) — Cero tolerancia a errores
3. Unit tests (Vitest/Jest) — < 2 minutos
4. Integration tests — Contra DB de prueba (Testcontainers/Docker)
5. Coverage check (c8/istanbul) — Mínimo 80% líneas, 70% branches
6. E2E critical paths (Playwright/Cypress) — Solo flows que no pueden fallar

### Coverage targets
- Líneas: ≥ 80%
- Branches: ≥ 70%
- Funciones: ≥ 85%
