Cómo resolver el error “target machine actively refused it” al ejecutar pruebas Selenium en Jenkins

Cuando las suites de pruebas Selenium funcionan en tu portátil pero se desploman sin aviso en un nodo de Jenkins, no es “mala suerte”: es una combinación de compatibilidad de binarios, puertos efímeros saturados y recursos de Windows que se agotan. En esta guía aprenderás a detectar la causa real y a estabilizar tus lotes de 20‑30 tests sin que vuelva a aparecer el temido “target machine actively refused it”.

Índice

Resumen del problema

En un servidor Windows alojado en AWS se lanza una pipeline Jenkins (o un simple pytest -n auto) que dispara decenas de casos en paralelo. De forma aleatoria, algunos navegadores fallan con:

MaxRetryError: HTTPConnectionPool(host='localhost', port=62210)…
urllib3.exceptions.NewConnectionError:
  Failed to establish a new connection: [WinError 10061]

El Visor de eventos muestra un crash de chromedriver.exe con la excepción 0xc0000005. La combinación instalada es Google Chrome 123.0.6312.59 + ChromeDriver 123.0.6312.59.

Por qué ocurre este fallo intermitente

  • Incompatibilidad de versiones  Chromedriver y Chrome comparten librerías nativas; una discrepancia menor basta para provocar segfault.
  • Asignación masiva de puertos locales  Cada driver = webdriver.Chrome() requiere un puerto libre. Con 30 instancias simultáneas, el rango 49152‑65535 puede agotarse o colisionar con el firewall local.
  • Fugas de memoria en la rama 123  Google reconoció problemas en Chrome 123 bajo Windows Server al ejecutar en headless.
  • Recursos del host  Si la VM tiene poca RAM/CPU, el sistema operativo termina procesos “silenciosamente”.

Diagnóstico rápido antes de tocar código

PruebaResultado esperadoInterpretación
Ejecutar 1 test aisladoÉxito constanteLa lógica del test no es el problema
Ejecutar 30 tests en secuencia (sin parallel)ÉxitoEl fallo se relaciona con concurrencia/recursos
Monitorear puertos con netstat -anoPicos en >60000Posible agotamiento de puertos efímeros
Activar --verbose en chromedriverStack‑trace antes del crashIndica fuga de memoria o incompatibilidad

Solución paso a paso

Actualizar Selenium y usar Selenium Manager

Selenium 4.11+ incorpora Selenium Manager, que descarga automáticamente la versión de driver que coincide con el navegador detectado:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add\_argument("--headless=new")           # Modo headless moderno
options.add\_argument("--disable-dev-shm-usage")
options.add\_argument("--no-sandbox")
options.add\_argument("--disable-gpu")
driver = webdriver.Chrome(options=options)       # Selenium Manager resuelve el driver 

Con esto eliminas el riesgo de instalar un chromedriver equivocado y reduces líneas de mantenimiento.

Controlar los recursos del servidor

  • Asignar CPU y RAM suficientes  Para 30 navegadores headless se recomiendan al menos 4 vCPU y 8 GB de RAM.
  • Desactivar antivirus en rutas de build  El análisis en tiempo real de Windows Defender ralentiza la apertura de cada driver.
  • Configurar Page‑File  Un archivo de paginación fijo (p. ej. 4 GB) evita que Windows “congele” procesos.

Optimizar la estrategia de concurrencia

En pytest‑xdist puedes reducir trabajadores:

pytest -n 10 --max-worker-restart=2 -q

o incluso probar -n logical, que adapta el paralelismo al número de cores.

En Jenkins, separa la ejecución en stages:

stage('UI Tests Batch 1') {
    steps { sh 'pytest -k "group1" -n 10' }
}
stage('UI Tests Batch 2') {
    steps { sh 'pytest -k "group2" -n 10' }
}

Evitar puertos en conflicto

Windows asigna puertos efímeros a partir de 49152. Para ampliarlo:

reg add "HKLM\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters" ^
/v MaxUserPort /t REG_DWORD /d 65534 /f

Tras reiniciar, el sistema podrá abrir hasta 16 382 puertos adicionales antes de rechazar conexiones.

Configurar timeouts y reintentos

Un driver saturado responde tarde. Amplía los márgenes e implementa reintentos:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from tenacity import retry, stopafterattempt, wait_fixed

@retry(stop=stop\after\attempt(3), wait=wait\_fixed(5))
def wait\for\element(driver, locator):
return WebDriverWait(driver, 20).until(
lambda d: d.find\_element(\*locator)
) 

Con pytest-rerunfailures (--reruns 2 --reruns-delay 5) puedes reejecutar casos esporádicos sin false negatives.

Registrar logs detallados

Guardar los logs como artefactos facilita el post‑mortem. Añade en tu pipeline:

CHROMEDRIVERVERBOSELOG=1
pytest --log-cli-level=INFO --log-file=test_run.log

Actualizar/retroceder versión de Chrome

Si actualizas a Chrome 124+ o retrocedes a 122, desaparecen fugas vistas en 123. Usa winget para versiones estables:

winget install --id Google.Chrome -v 124.0.6367.60

Considerar una arquitectura distribuida

En lugar de abrir decenas de ventanas en la misma VM, levanta un Selenium Grid (o utiliza AWS Device Farm). Cada nodo ejecuta 3‑5 navegadores y devuelve resultados al hub central, evitando conflictos de puertos.

Checklist de mejores prácticas

ÍtemAcción recomendada
VersionesChrome y ChromeDriver idénticos o usar Selenium Manager
ParalelismoLímitar a nº cores o lotes de 10
RAM/CPU>8 GB RAM y 4 vCPU para 30 navegadores
PuertosAmpliar MaxUserPort y revisar firewall
Timeoutsimplicitwait 5‑10 s, pageload_timeout 60 s
Liberar procesosdriver.quit() en tearDown
LogsEnlace a test_run.log como artefacto Jenkins
Versión ChromeActualizar a >=124 o retroceder a 122
GridDistribuir carga en varios nodos

Preguntas frecuentes

¿Puedo usar Edge en vez de Chrome? Sí. EdgeDriver tiene menos problemas de puertos en Windows Server 2022, pero asegúrate de emparejar versiones.

¿Por qué ocurre solo en AWS y no en mi PC? Las instancias pequeñas (t3.medium) limitan bursts de CPU; cuando la métrica Credit balance llega a cero, el rendimiento se desploma y los drivers no responden.

¿Reciclar el driver acelera los tests? No. Reutilizar webdriver entre casos ahorra segundos, pero aumenta fugas. Crea y destruye un driver por fixture siempre que tengas recursos.

Conclusión

La combinación de compatibilidad estricta, control de recursos y una estrategia de paralelismo adecuada resuelve casi todos los cierres inesperados de ChromeDriver. Inicia tus pruebas con versiones correctas, monitoriza puertos/CPU, amplía timeouts y registra logs: tu pipeline Jenkins dejará de fallar con el odioso mensaje “target machine actively refused it”. Y si la carga crece, escalar a un Selenium Grid evitará cuellos de botella mientras mantienes la estabilidad de tus entregas CI/CD.

Índice