Tutoriales

Patrones avanzados de Puppeteer + CaptchaAI para QA propia

Alcance seguro: solo tus entornos de QA, staging y preproducción autorizados - no sitios de terceros.

Los tests de Puppeteer con CAPTCHA rara vez fallan por el CAPTCHA: fallan por concurrencia sin control, contextos que nadie cierra y métricas que no existen. Cuatro patrones - concurrencia acotada, reuso de BrowserContext, cierre en finally y trazas por intento - estabilizan una suite en CI.

La facturación condiciona el diseño: CaptchaAI cobra por thread concurrente, no por resolución. BASIC ($15/mes, 5 threads) cubre un runner de QA; ADVANCE ($90/mes, 50 threads), varias suites. Tu límite de concurrencia y tus threads son el mismo número.

Concurrencia acotada por caso de QA

Acota la concurrencia por caso_qa en vez de lanzar todas las páginas a la vez: no saturas staging ni agotas los threads del plan. Regla práctica: máximo de páginas simultáneas igual a tus threads menos uno, reservado para reintentos - 4 con BASIC, 49 con ADVANCE. Si la cola crece más rápido de lo que se vacía, aparece ERROR_NO_SLOT_AVAILABLE intermitente.

Reuso de BrowserContext y cierre en finally

Levantar un navegador por test es cómodo y caro. Comparte un BrowserContext entre pasos que no modifican estado - navegación, lectura de formularios, render - y reserva uno limpio para los que escriben datos. El límite lo pone la memoria: 50-100 MB por página, entre 5 y 10 con 8 GB.

El reverso es el cierre. Un handle filtrado no se nota hasta que el runner se queda sin memoria: cierra páginas y contextos en un bloque finally, también cuando el test ya falló.

Configuración de navegador idéntica en todos los runners

Muchos "funciona en local, falla en CI" no son bugs: son diferencias de viewport, idioma o user-agent. Congélala en una función:

from selenium import webdriver

def make_driver(headless: bool = True) -> webdriver.Chrome:
    options = webdriver.ChromeOptions()
    if headless:
        options.add_argument('--headless=new')
    options.add_argument('--window-size=1280,800')
    options.add_argument('--lang=es-ES')
    return webdriver.Chrome(options=options)

Fija --lang=es-ES si sirves contenido localizado: un portal en español puede renderizar widgets distintos según el idioma, y con ello cambian tus selectores.

Cómo entra CaptchaAI en el flujo

El patrón es el mismo en cualquier framework de pruebas:

  1. Tu test detecta el widget en tu aplicación (QA, staging o preproducción).
  2. Envía a CaptchaAI los datos públicos del widget (sitekey, URL, tipo).
  3. CaptchaAI devuelve un token válido para esa página.
  4. Tu test lo inyecta en el campo correspondiente y envía el formulario.
  5. Tu backend lo verifica contra el proveedor, igual que con una persona.

Si arrancas de cero, el inicio rápido de CaptchaAI cubre la primera llamada.

Ejemplo: portal de trámites en staging

Caso habitual en equipos de España y Latinoamérica: una aplicación de cita previa o de trámites administrativos protegida con Cloudflare Turnstile, cuyo formulario de alta hay que verificar en cada release. La suite corre de noche sobre staging, con 5 casos en paralelo sobre BASIC.

Con el paso de CAPTCHA aislado en una función con reintentos, un cambio de sitekey rompe un solo punto en vez de cinco tests sin relación. Para los parámetros de cada tipo, consulta resolver reCAPTCHA v2 con la API y resolver Cloudflare Turnstile con la API. Aunque el entorno sea tuyo, usa datos sintéticos y respeta la normativa aplicable (GDPR y LOPDGDD, LFPDPPP en México).

Métricas que hacen visible una regresión

Sin métricas por intento, un test intermitente es indistinguible de uno roto. Instrumenta:

  • Tiempo de resolución por intento - de la solicitud al token entregado.
  • Tasa de éxito por endpoint propio - verificaciones de backend correctas sobre el total.
  • Distribución de errores - por código (ERROR_*, tiempos de espera, red).
  • Latencia extremo a extremo - render, resolución y respuesta del backend.

Conserva trazas (logs, capturas, HAR) para reproducir incidentes esporádicos. Si el paso falla pero la API responde bien, depurar tests de navegador cuando la API funciona acota el problema.

Higiene de la suite

Solución de problemas

Síntoma Acción recomendada
El test no detecta el widget Revisa selectores y tiempos de espera en staging
ERROR_NO_SLOT_AVAILABLE Reintenta con backoff y baja la concurrencia
El backend rechaza el token Compara action y sitekey con tu configuración
Funciona en local pero falla en CI Iguala viewport, idioma y user-agent
El runner se queda sin memoria Menos páginas simultáneas; revisa el finally
Tiempos muy variables Revisa el reparto de threads entre suites

Preguntas frecuentes

¿Cuántas páginas de Puppeteer puedo abrir en paralelo?

Manda el más bajo de dos límites: la memoria del runner (50-100 MB por página; 5 a 10 con 8 GB) y los threads de tu plan, 5 con BASIC ($15/mes) o 50 con ADVANCE ($90/mes).

¿Necesito un plan distinto para QA y para producción?

No necesariamente, pero sí una clave distinta: compartirla mezcla los threads de la suite nocturna con el tráfico real y oculta el origen de un pico de latencia.

¿Qué tipos de CAPTCHA puedo cubrir en mis tests?

Cubres reCAPTCHA v2 y v3, Cloudflare Turnstile y Challenge, GeeTest v3, imagen/OCR y grid, más CaptchaFox (beta), Friendly Captcha (beta) y Lemin (beta). Quedan fuera hCaptcha y FunCaptcha (Arkose Labs), no compatibles; GeeTest v4 figura como próximamente.

¿Puedo usar estos patrones sobre sitios que no son míos?

No. Todo lo anterior asume entornos de QA y staging autorizados; resolver CAPTCHA en sitios que no controlas puede infringir sus términos de servicio.

¿Qué hago cuando un test falla solo de vez en cuando?

Aísla el paso en una función con reintentos acotados y backoff exponencial, y registra métricas por intento: así distingues un fallo de red, un tiempo de espera agotado y un error de configuración propio.

Valida tus integraciones CAPTCHA en entornos propios con CaptchaAI.

Los comentarios están deshabilitados para este artículo.