Módulo 3: Integration Testing & Estrategias No-Determinísticas

1. Introducción: Integration Testing para AI

Descripción

Los módulos 1-2 resolvieron testing de forma determinística con mocks. Pero hay escenarios donde necesitas el LLM real: validar que el modelo produce outputs útiles, detectar regresiones al cambiar de modelo, verificar el flujo end-to-end. El problema: con LLM real, el mismo input puede producir outputs diferentes. Este módulo enseña las estrategias probadas para manejar eso — sin perder la confianza en tu test suite.


El límite de los mocks

En el Módulo 2 aprendiste que los mocks son poderosos: rápidos, gratuitos, determinísticos. Pero tienen un límite fundamental:

# Con mock, sabes que:
# ✅ Tu parser maneja bien el JSON
# ✅ Tu processor normaliza el score correctamente
# ✅ La estructura del output cumple el contrato
# ✅ Los errores de API se manejan gracefully

# Con mock, NO sabes:
# ❌ ¿El LLM realmente entiende el prompt?
# ❌ ¿El resumen es coherente con el texto original?
# ❌ ¿El modelo sigue produciendo la misma calidad después de una actualización?
# ❌ ¿El prompt funciona bien para todos los idiomas e inputs edge?

Los integration tests con LLM real llenan este gap. No reemplazan los unit tests — los complementan.


El desafío central: non-determinism

El problema fundamental de testear con LLM real:

# Mismo input, dos ejecuciones distintas:
text = "Python es un lenguaje de programación interpretado y de alto nivel."

result_1 = summarize(text)
# → {"summary": "Python es un lenguaje de alto nivel interpretado.", "score": 0.9}

result_2 = summarize(text)
# → {"summary": "Python: lenguaje interpretado de alto nivel.", "score": 0.92}

# Ambos son CORRECTOS. Pero son DIFERENTES.
# assert result_1 == result_2  → FALLA
# assert result_1["summary"] == "Python es un lenguaje interpretado."  → FALLA también

Las estrategias de este módulo resuelven exactamente este problema: cómo hacer assertions que sean significativas cuando el output varía.


El espectro de assertions

Hay un continuo de estrategias, desde la más estricta a la más flexible:

Más estricto                                              Más flexible
─────────────────────────────────────────────────────────────────►

[Igualdad exacta]  [Contiene]  [Regex]  [Propiedades]  [Semántica]
     result ==         kw in      re.     len in range   similarity
    "exact text"      result    match()      0.85-0.95

La regla: Usa la assertion más estricta que sea estable. Si el output exacto puede variar, sube un nivel en el espectro hasta que la assertion sea robusta sin perder valor de detección.

EstrategiaCuándo usarla
Igualdad exactaCon mocks (determinístico)
Contiene keywordCuando el output DEBE mencionar algo específico
RegexFormato o estructura parcial
Propiedades (rango, tipo)Invariantes que siempre se cumplen
Similitud semánticaCuando el significado importa, no las palabras exactas

Cuándo usar integration tests

EscenarioUsa MockUsa LLM Real
Desarrollo diario❌ (caro, lento)
Validar estructura del outputOpcional
Validar calidad semántica
Detectar model drift
CI en cada commit
CI en main/pre-release✅ (con budget cap)
Debugging de output incorrectoOpcional

Regla de proporciones: 80% unit tests (mock), 20% integration tests (LLM real). Los integration tests son el complemento estratégico, no la base.


El costo como constraint real

Los integration tests cuestan dinero. Este es un constraint de diseño, no un detalle:

Ejemplo real:
- 100 integration tests
- Cada test: 500 tokens input + 200 tokens output
- Modelo: gpt-4o-mini ($0.15/1M input, $0.60/1M output)

Costo por run:
  Input:  100 × 500 × $0.15/1M = $0.0075
  Output: 100 × 200 × $0.60/1M = $0.0120
  Total:  ~$0.02 por run

Con 50 commits/día: $1/día = $30/mes SOLO en tests

Con gpt-4o ($2.50/1M input, $10/1M output):
  Mismo setup: ~$0.25 por run × 50 = $12.50/día = $375/mes

Por esto los budget controls no son opcionales — son parte del diseño de la test suite.


Las cinco estrategias del módulo

Este módulo enseña cinco estrategias concretas:

Estrategia 1: Tests End-to-End (Cápsula 2)

Ejecutar el flujo completo con LLM real. Assertions flexibles sobre el output. Budget controls obligatorios.

Estrategia 2: Decision Framework Mock vs Real (Cápsula 3)

Criterios claros para decidir cuándo cada tipo de test aporta más valor. Configuración por entorno.

Estrategia 3: Semantic Similarity Assertions (Cápsula 4)

Comparar el significado del output con embeddings. Elimina la frágil igualdad de strings manteniendo assertions significativas.

Estrategia 4: Property-Based Testing (Cápsula 5)

Definir invariantes que siempre deben cumplirse. Hypothesis genera inputs automáticamente. Más eficaz que casos de test manuales.

Estrategia 5: Flaky Test Management (Cápsula 6)

Retry logic, tolerance thresholds, quarantine. Para cuando la varianza del LLM hace que los tests fallen ocasionalmente.


La transformación mental del módulo

Antes de este módulo:
  → "No puedo testear con LLM real — el output siempre es diferente"
  → "Los integration tests son demasiado caros"
  → "Mis tests de AI son flaky, es normal"

Después de este módulo:
  → "Non-determinism es manejable con las estrategias correctas"
  → "Los integration tests son estratégicos — pocos, bien elegidos"
  → "Flakiness es una señal que requiere atención, no algo que se ignora"

Prerequisitos del módulo

Antes de comenzar, asegúrate de tener del Módulo 2:

# 1. Suite de unit tests funcionando
pytest -m unit -v  # Debe pasar 100%

# 2. La app de referencia funcionando
python -c "from app.sentiment import analyze_sentiment; print('OK')"

# 3. API key configurada (para integration tests)
echo $OPENAI_API_KEY  # Debe mostrar la key

# 4. Dependencias adicionales para este módulo
pip install pytest-rerunfailures hypothesis sentence-transformers numpy

Roadmap del módulo

#CápsulaTipoDependency
01IntroducciónConceptualM2 completo
02Tests end-to-endTécnicoOpenAI API key
03LLM real vs mocksEstrategia
04Semantic similarity assertionsTécnicoOpenAI embeddings o sentence-transformers
05Property-based testingTécnicoHypothesis
06Flaky test managementTécnicopytest-rerunfailures
07Proyecto Integration Test SuiteProyectoTodo lo anterior
08Resumen y troubleshootingCierre

Conexión con los módulos anteriores

Módulo 1: Configuró pytest, definió markers, smoke tests
          ↓
Módulo 2: Unit tests con mocks, contract tests, parsers
          ↓
Módulo 3: Integration tests, LLM real, non-determinism strategies
          ↓
Módulo 4: Guardrails implementados y testeados con M3 strategies

El Módulo 3 no solo añade integration tests — también sirve como base para los módulos siguientes donde testearás guardrails, validaciones de input/output, y comportamientos de seguridad que requieren el LLM real para ser validados correctamente.


Ejercicios

Ejercicio 1: Identificar el gap de los mocks

Para tu app actual, identifica 3 cosas que el mock NO puede validar pero que son importantes para producción:

Ver guía

Ejemplos comunes:

  1. Calidad semántica: "¿El resumen captura los puntos clave del texto original?" — El mock solo valida estructura.
  2. Prompt robustez: "¿El prompt funciona igual de bien para textos en inglés, español, y portugués?" — El mock retorna lo que tú le programas, no lo que el LLM real produciría.
  3. Model drift: "Después de actualizar de gpt-4o a gpt-4o-mini, ¿el output sigue siendo suficientemente bueno?" — Necesitas comparar con el LLM real.
  4. Edge cases reales: "Para textos con emojis, lenguaje informal o jerga, ¿el parser sigue funcionando?" — El LLM real produce variaciones que no anticipas en mocks.

Ejercicio 2: Calcular el costo de tu test suite

Estima el costo mensual de correr integration tests para tu app:

  • 20 integration tests
  • Cada test: 600 tokens input + 150 tokens output
  • Modelo: gpt-4o-mini
  • Frecuencia: 2 veces al día (CI en main)
Ver cálculo
Por run:
  Input:  20 × 600 × $0.15/1M  = $0.0018
  Output: 20 × 150 × $0.60/1M  = $0.0018
  Total:  ~$0.0036 por run

Por mes (30 días × 2 runs/día):
  $0.0036 × 60 = ~$0.22/mes

Con gpt-4o (17x más caro):
  $0.22 × 17 ≈ $3.74/mes

Conclusión: Con gpt-4o-mini es muy accesible.
Con gpt-4o, considera ejecutar menos frecuentemente.

Ejercicio 3: Diseñar el espectro de assertions

Para un test E2E de resumen de texto, diseña una assertion en cada nivel del espectro:

Ver solución
# Nivel 1: Igualdad exacta (frágil con LLM real)
assert result["summary"] == "Python es un lenguaje interpretado y de alto nivel."

# Nivel 2: Contiene keyword (más estable)
assert "python" in result["summary"].lower()

# Nivel 3: Regex (formato específico)
assert re.search(r'\w+ es \w+', result["summary"])  # Tiene forma "X es Y"

# Nivel 4: Propiedades (invariantes)
assert 10 <= len(result["summary"].split()) <= 100  # Entre 10 y 100 palabras
assert 0 <= result["confidence"] <= 1

# Nivel 5: Similitud semántica
assert_semantically_similar(
    result["summary"],
    "Python es un lenguaje de programación de alto nivel",
    threshold=0.8
)

Recomendación: Para integration tests con LLM real, usa niveles 2-5. El nivel 1 solo con mocks.


Ejercicio 4: Non-determinism en tu proyecto

¿Cuál es el test más "frágil" que tienes actualmente? ¿Qué estrategia del módulo lo haría más robusto?

Ver guía

Evaluación:

  • Si el test hace assert result == "texto exacto" → Cambiar a semantic similarity o property-based
  • Si el test falla frecuentemente por timeout → Añadir retry logic y skip condicional
  • Si el test llama al LLM real en cada commit → Convertir a mock (o limitarlo con skip por env var)
  • Si el test verifica que "el resumen es bueno" → Definir la propiedad específica: longitud, keywords, similitud

Ejercicio 5: Planificar tu integration test suite

Antes de escribir el código, planifica:

  1. ¿Cuántos integration tests tendrá tu suite?
  2. ¿Cuándo se ejecutarán (cada commit / en main / nightly)?
  3. ¿Cuál es el budget máximo por run?
  4. ¿Qué estrategia usarás para cada test?
Ver plantilla de planificación
Integration Test Suite Plan:

Tests: 10-15 (regla: pocos y bien elegidos)
Ejecución: En merge a main (no en cada commit)
Budget: $0.10 máximo por run

Test 1: E2E flujo principal (resumen)
  → Estrategia: propiedades (longitud, estructura)
  → Modelo: gpt-4o-mini
  → Tokens estimados: 800

Test 2: Calidad semántica del resumen
  → Estrategia: semantic similarity threshold=0.8
  → Modelo: gpt-4o-mini
  → Tokens estimados: 1200 (incluye embeddings)

Test 3: Flujo con input edge (texto muy corto)
  → Estrategia: propiedades + manejo de error
  → Modelo: gpt-4o-mini
  → Tokens estimados: 300

...

Resumen

  • Integration tests complementan (no reemplazan) los unit tests con mock
  • Non-determinism es manejable con las estrategias correctas: semantic similarity, property-based, flaky management
  • El espectro de assertions: de igualdad exacta a similitud semántica — usa la más estricta que sea estable
  • Costo es un constraint real — budget controls desde el diseño, no como afterthought
  • Proporción: 80% unit tests, 20% integration tests estratégicos

Recursos adicionales

  1. Testing ML Systems — Google — Prácticas de equipos grandes
  2. OpenAI Pricing — Precios actuales para calcular budget
  3. The Test Pyramid — Martin Fowler — Proporciones de tipos de tests
  4. Non-determinism in Tests — Martin Fowler — Estrategias generales
  5. Módulo 2: Unit Testing LLM Applications — Prerequisito
  6. Hypothesis Documentation — Para property-based testing (Cápsula 5)
  7. sentence-transformers — Embeddings locales para semantic assertions