BookinglyTech News
Software

Un patrón de Page Object con reintentos para Selenium en SPAs dinámicas

El enfoque encapsula cada interacción en un método que reintenta la localización cuando el DOM sustituye el nodo y espera por estados de datos en lugar de por la mera presencia del elemento.

2 min de lecturaDev.to0 vistas

Un ejemplo en Java con Selenium plantea cómo atajar la flakiness de las suites que atacan aplicaciones de una sola página con renderizado dinámico. La idea no es nueva, pero está bien acotada: meter toda la lógica de interacción en un Page Object que reintente cuando el navegador invalida una referencia y que espere por el dato, no solo por el nodo.

El problema de fondo es conocido por cualquiera que haya peleado con CI en un SPA. Los componentes se vuelven a renderizar, los datos llegan de forma asíncrona y cualquier refactor del front cambia el árbol. Las esperas explícitas habituales se quedan cortas cuando el elemento no es que tarde, es que ha sido reemplazado por otro. De ahí las dos excepciones que llenan los informes: StaleElementReferenceException y NoSuchElementException.

Reintentar la búsqueda, no la interacción

El método central del patrón envuelve un WebDriverWait con una condición de elemento clicable y captura StaleElementReferenceException en un bucle de dos intentos. Lo importante es que el reintento vuelve a resolver el localizador desde cero, con el By original, en lugar de reutilizar el WebElement que ya está muerto. Si se agotan los intentos, lanza una excepción con el localizador en el mensaje, que es lo que uno quiere leer en el log.

Sobre esa base, el Page Object expone métodos de negocio en lugar de exponer el driver. El clic, la lectura de un título o la consulta de un valor pasan siempre por la misma puerta, y el resto del test no sabe nada de esperas ni de reintentos.

Esperar por el dato, no por el hueco

El segundo detalle interesante está en cómo se leen los valores. En vez de esperar a que el elemento exista, el código espera a que su texto deje de estar vacío. Es la diferencia entre saber que el contenedor está en el DOM y saber que la aplicación ya ha pintado lo que había que pintar.

Para widgets que dependen unos de otros, la espera se hace con un texto parcial esperado sobre el selector del segundo componente. Así el test avanza solo cuando el primer resultado ha propagado al segundo, y no cuando el primero ya se ve. El código descarta explícitamente Thread.sleep(), que es la forma más rápida de convertir una suite lenta en una suite lenta e inestable.

El patrón no es una bala de plata. El número de reintentos hay que ajustarlo a cada aplicación, y esperar por texto parcial puede dar falsos positivos si el texto es demasiado genérico. Pero para equipos con suites que se caen cada dos ejecuciones, mover la lógica de reintento y de espera al Page Object es más limpio que sembrar de condiciones cada caso de prueba.