Sesión 6 de mi lab de Selenium + Java: Esperas Implícitas, Explícitas y Fluent Waits

Por qué Thread.sleep hace tests lentos, cuándo NO usar implicitWait, cómo WebDriverWait sincroniza con el DOM. Uso de FluentWait. Decisiones.

Código Java mostrando los cuatro mecanismos de espera: Thread.sleep, implicitlyWait, WebDriverWait y FluentWait con sus configuraciones
Los 4 mecanismos de espera en un vistazo: de la pausa ciega al control fino.

Idea central del post

En Selenium casi todo lo “inestable” viene de lo mismo: el DOM cambia antes de que tu test intente interactuar. Las esperas existen para sincronizar el test con la app.

Este post documenta lo que investigué y probé sobre esperas en Selenium.


Cuántos tipos de esperas hay

Existen 3 maneras de esperas:

Mecanismos de espera en tests UI
   │
   ├─ Pausa fija (Java)
   │    └─ Thread.sleep
   │
   ├─ Espera implícita (Selenium)
   │    └─ implicitWait
   │
   └─ Esperas explícitas (Selenium)
        ├─ WebDriverWait
        └─ FluentWait

A continuación se las describe.


Pausa fija (Java): Thread.sleep

El primer mecanismo que suele aparecer cuando algo “no carga a tiempo” es Thread.sleep.

Thread.sleep(2000);

A simple vista parece una solución válida:
“Espero 2 segundos y después sigo.”
Yo mismo lo usé al principio de este lab.

Sin embargo, Thread.sleep no es una espera de Selenium.
Es una pausa del hilo de ejecución en Java.


Qué hace realmente Thread.sleep

  • Detiene el hilo actual durante un tiempo fijo.
  • No sabe nada del DOM.
  • No verifica condiciones.
  • No reintenta nada.
  • Pase lo que pase en la aplicación, el hilo duerme igual.

En otras palabras:

Thread.sleep espera tiempo, no estado.

El primer problema práctico: try / catch

Cuando intenté usar Thread.sleep en el test, Java no me dejó compilar:

IntelliJ IDEA mostrando error de compilación en Thread.sleep: unreported exception InterruptedException must be caught or declared
Thread.sleep requiere manejar InterruptedException — ruido técnico que no aporta al test.

El error era algo así como:

unreported exception java.lang.InterruptedException;
must be caught or declared to be thrown

Esto ocurre porque Thread.sleep lanza una checked exception: InterruptedException.

Java obliga a:

  • manejarla con try / catch
try {
    Thread.sleep(2000);
} catch (InterruptedException e) {
    throw new RuntimeException(e);
}
  • o declararla en la firma del método:
Código Java con throws InterruptedException en la firma del método, resaltado con flecha verde
Al agregar throws InterruptedException, el código compila y el sleep funciona.

Al agregar throws InterruptedException, el código compila y el sleep funciona.


Por qué esto ya es una señal de alerta

En ese momento el test empezó a “saber” cosas que no tienen nada que ver con el negocio que estoy probando:

  • interrupciones de hilos
  • excepciones técnicas
  • detalles de concurrencia de Java

Todo eso no aporta valor al test.

El objetivo del test es:

“Login funciona correctamente”
no
“Gestionar interrupciones de hilos”.

El problema real de Thread.sleep

Más allá del try / catch o agregar throws InterruptedException, el problema de fondo es este:

Con:

Thread.sleep(2000);
  • Si la página carga en 200 ms → perdí 1.8 s innecesarios
  • Si la página carga en 3 s → el test falla igual

Thread.sleep:

  • hace los tests más lentos
  • no elimina flakiness: Flakiness es la falta de determinismo en los tests automáticos: fallan o pasan de forma inconsistente sin cambios en el código, generalmente por problemas de sincronización, estado o dependencias externas. Es decir: test no es confiable.
  • oculta problemas reales de sincronización

Conclusión sobre Thread.sleep

Aunque Thread.sleep “funcione” y sea tentador al principio, no sincroniza el test con la aplicación.

Además:

  • agrega ruido (try / catch o throws)
  • expone detalles técnicos innecesarios
  • hace que el test espere tiempo en lugar de esperar condiciones

Por eso, en un framework de Selenium, Thread.sleep debería:

  • usarse solo para debug puntual
  • o directamente evitarse en tests normales

En las siguientes secciones paso a las esperas reales de Selenium, que esperan estado del DOM, no tiempo fijo.


Espera implícita (Selenium): implicitWait

El siguiente mecanismo que ofrece Selenium es la espera implícita.

A diferencia de Thread.sleep, esta sí pertenece a Selenium y se configura una sola vez sobre el WebDriver:

DriverManager.initDriver();
WebDriver driver = DriverManager.getDriver();

driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));

¿Por qué ahí y no en otro lado?

Porque la espera implícita:

  • es una configuración del driver
  • no depende de clicks
  • no depende de By
  • no depende de acciones concretas
  • afecta todas las búsquedas de elementos

No es una instrucción “paso a paso”, es un default global.

Con esto le decimos a Selenium:

“Cada vez que intentes buscar un elemento, esperá hasta 10 segundos si no aparece de inmediato.”

Cómo funciona realmente la espera implícita

  • Se aplica a todas las búsquedas de elementos (findElement, findElements)
  • Selenium reintenta encontrar el elemento hasta que aparece o vence el timeout
  • No espera condiciones específicas (visible, clickeable, etc.)
  • Solo espera que el elemento exista en el DOM

Es decir:

La espera implícita actúa solo en el momento de localizar elementos.

En mi caso, probé en colocar una ruta incorrecta de un elemento y ver si implicitWait actuaba:

Código mostrando implicitlyWait configurado y un locator incorrecto LoginPanel0_Username_MALLLL para probar el comportamiento de la espera implícita
Con un locator incorrecto, la espera implícita consume todo el timeout buscando el elemento.

y efectivamente al NO encontrar el elemento el test empezó a comer tiempo buscándolo.

Pero cuando el elemento si existe, cuando si encuentra la ruta, no espera ese tiempo máximo que se le asigna.

Proceso que hace:

By usuarioInput = By.id("LoginPanel0_Username");
└─ Caso A – Elemento YA existe
    │
    ├─ ✔️ Selenium lo encuentra
    ├─ ✔️ Devuelve el elemento
    └─ ❌ No espera nada

By usuarioInput = By.id("LoginPanel0_Username_MALLLL");
└─ Caso B – Elemento NO existe todavía
    │
    ├─ ✔️ Selenium empieza a reintentar
    ├─ ✔️ Hasta que aparece
    └─ ✔️ O hasta que vence la implicit wait
       👉 Acá sí actúa la espera implícita

findElement dentro de condiciones de WebDriverWait

La espera implícita solo se activa cuando Selenium ejecuta internamente:

driver.findElement(...)

Si no hay findElementno hay espera.

En mi caso, no tengo una línea con findElement tipeado, sino que uso:

Código Java usando WebDriverWait con visibilityOfElementLocated y elementToBeClickable, flechas verdes señalando cada condición
Esperas explícitas en acción: visibilidad para inputs, clickeabilidad para el botón.

Y Sí, internamente en estas líneas Selenium termina ejecutando un findElement.

WebElement usuario =
wait.until(ExpectedConditions.visibilityOfElementLocated(usuarioInput));
Muchas condiciones de WebDriverWait que reciben un By ejecutan internamente driver.findElement de forma repetida hasta que la condición se cumple o vence el timeout.

Ventaja aparente (y por qué engaña)

A primera vista parece cómoda:

  • no hay try / catch ni throws
  • no hay waits repetidos en el código
  • todo “parece más estable”

Pero esa simplicidad es engañosa.


El problema principal: falta de control

Con espera implícita, Selenium solo espera una cosa:

Que el elemento exista en el DOM.

Nada más.

No espera:

  • que sea visible
  • que sea clickeable
  • que esté habilitado
  • que no tenga overlay arriba

Ejemplo concreto (paso a paso)

Imaginá este flujo:

1_ La página empieza a cargar
2_ Selenium ejecuta:

driver.findElement(By.id("LoginPanel0_LoginButton"))

3_ El botón:

    • ya existe en el DOM
    • pero todavía:
      • está oculto (display: none)
      • o deshabilitado
      • o tapado por un loader

👉 Para la espera implícita, eso ya alcanza.

Selenium dice:

“Elemento encontrado. Mi trabajo terminó.”

Qué pasa después (y por qué falla)

El test sigue:

loginButton.click();

Pero:

  • el botón no es clickeable
  • Selenium lanza excepción
  • el test falla

Y desde afuera parece:

“La implicit wait no funcionó”.

Pero en realidad:

Sí funcionó… para lo único que sabe hacer.

Con espera implícita, Selenium considera suficiente que el elemento exista en el DOM.
Si el elemento ya está presente pero todavía no es visible o clickeable, la espera implícita se da por cumplida y el test continúa, aunque la UI todavía no esté lista.

Cuando hable acerca de las esperas explicitas quedará claro que:

Implícita = “¿existe?”
Explícita = “¿está listo para interactuar?”

Otro problema serio: mezclar implícita y explícita

Este es el error más común.

Si combinás:

implicitWait(10s)
WebDriverWait(10s)

podés terminar con:

  • esperas acumuladas
  • tiempos inesperados
  • tests lentos y difíciles de razonar

En proyectos reales, esto genera flakiness difícil de diagnosticar.


Buenas prácticas (lo que se hace en serio)

En la mayoría de frameworks profesionales:

  • ❌ no se usa espera implícita
  • ❌ no se mezcla con explícitas
  • ✔️ se usan solo esperas explícitas
  • ✔️ se espera una condición concreta, en un lugar concreto

La espera implícita queda relegada a:

  • demos
  • ejemplos muy simples
  • proyectos muy pequeños

Conclusión

La espera implícita no es mala, pero es demasiado genérica.

Puede servir al principio, pero:

  • no da control fino
  • no expresa intención
  • no escala bien en un framework

Por eso, en este lab decidí no usar espera implícita y pasar directamente a esperas explícitas, que permiten sincronizar el test con el estado real de la aplicación.


Esperas explícitas (Selenium): WebDriverWait

Después de probar Thread.sleep y la espera implícita, llegué a las esperas explícitas, que son el mecanismo recomendado en Selenium para sincronizar correctamente un test con la aplicación.

La espera explícita se basa en una idea simple pero poderosa:

Esperar una condición concreta,
sobre un elemento concreto,
durante un tiempo máximo.

Cómo se usa WebDriverWait

Vamos por partes

1️⃣ Primero se crea el wait indicando:

  • el WebDriver
  • el tiempo máximo de espera
Línea de código creando WebDriverWait con driver y timeout de 10 segundos
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

¿wait es una palabra reservada?
No. wait no es palabra reservada, es solo el nombre de una variable.
Podrías llamarla:
WebDriverWait espera;
WebDriverWait loginWait;
WebDriverWait miWait;

Se llama wait por convención, nada más.

2️⃣¿Qué es WebElement?

Línea de código usando wait.until con ExpectedConditions.visibilityOfElementLocated
WebElement usuario = ...

WebElement no es una palabra reservada de Java.

Es una interfaz de Selenium (org.openqa.selenium.WebElement).
Representa un elemento del DOM (un input, botón, link, etc.).

Cuando tenés un WebElement, Selenium ya:

  • encontró el elemento
  • sabe dónde está
  • puede interactuar con él

Por eso después podés hacer:

Código mostrando métodos de WebElement después de la espera: clear, sendKeys y click
Una vez que el elemento es visible, ya es seguro interactuar con él.
usuario.clear();
usuario.sendKeys("admin");
usuario.click();

3️⃣ ¿Qué es .until()?

wait.until(...)

until es un método de WebDriverWait.

Conceptualmente significa:

“Seguí intentando hasta que esta condición se cumpla
o hasta que se agote el tiempo máximo.”

No devuelve true o false.

👉 Devuelve el resultado de la condición cuando se cumple.

4️⃣ ¿Qué es ExpectedConditions?

ExpectedConditions.visibilityOfElementLocated(...)

ExpectedConditions:

  • es una clase de Selenium
  • contiene métodos estáticos
  • cada método representa una condición esperable del DOM

Ejemplos:

  • visible
  • clickeable
  • presente
  • invisible
  • texto presente
  • url contiene algo

Son básicamente reglas predefinidas que Selenium sabe evaluar.

5️⃣ ¿Qué es visibilityOfElementLocated?

visibilityOfElementLocated(usuarioInput)

Esta condición significa exactamente esto:

“El elemento:existe en el DOMy es visible para el usuario”

Internamente Selenium:

  • ejecuta driver.findElement(usuarioInput)
  • verifica isDisplayed()
  • si no se cumple, espera y reintenta

Por eso no alcanza con que el elemento exista.

6️⃣ ¿Qué son las “condiciones”?

Una condición es simplemente una regla que debe cumplirse antes de seguir.

Ejemplos de condiciones:

  • “está visible”
  • “es clickeable”
  • “ya no está en pantalla”
  • “la URL cambió”
  • “el texto apareció”

La espera explícita no espera tiempo,
espera que una condición sea verdadera.

7️⃣ ¿Qué es usuario al final?

Volvamos a la línea completa:

WebElement usuario =
    wait.until(ExpectedConditions.visibilityOfElementLocated(usuarioInput));

Esto se puede leer así:

“Esperá hasta que el input de usuario sea visible,
y cuando lo sea, devolveme ese elemento.”

Entonces:

  • usuario es un WebElement
  • ya fue encontrado
  • ya es visible
  • ya es seguro interactuar

Por eso después podés hacer sin problemas:

usuario.clear();
usuario.sendKeys("admin");

8️⃣ ¿Por qué puedo usar .clear(), .sendKeys(), .click()?

Porque todos esos métodos pertenecen a WebElement.

Un WebElement expone operaciones típicas de UI:

  • escribir
  • borrar
  • hacer click
  • leer texto
  • verificar visibilidad

👉 Primero esperás el estado correcto
👉 después interactuás

Ese es el orden sano en Selenium.


Qué hace realmente WebDriverWait

A diferencia de Thread.sleep, no duerme el hilo sin pensar.

Internamente, WebDriverWait:

  • reintenta la condición varias veces
  • ejecuta la lógica cada ~500 ms (una de sus características llamada polling viene por defecto así)
  • evalúa si la condición se cumple
  • termina apenas se cumple, sin esperar el tiempo máximo
  • lanza TimeoutException si el tiempo se agota

Es decir:

WebDriverWait espera estado del DOM, no tiempo fijo.

findElement dentro de WebDriverWait

Aunque en el test no escribamos explícitamente driver.findElement(...),
muchas condiciones de ExpectedConditions lo usan internamente.

Por ejemplo:

ExpectedConditions.visibilityOfElementLocated(By locator)

Internamente hace algo equivalente a:

  • buscar el elemento con findElement
  • verificar que sea visible
  • repetir el proceso hasta que se cumpla la condición o venza el timeout

Por eso:

  • si el elemento todavía no existe → espera
  • si existe pero no es visible → sigue esperando
  • si existe y es visible → continúa el test

Esto explica por qué las esperas explícitas son mucho más robustas que la implícita.


Condiciones más usadas en WebDriverWait

Algunas de las condiciones más comunes son:

  • visibilityOfElementLocated
    Espera que el elemento exista y sea visible.
  • elementToBeClickable
    Espera que el elemento exista, sea visible y esté habilitado.
  • presenceOfElementLocated
    Espera solo que el elemento exista en el DOM.
  • invisibilityOfElementLocated
    Espera que un elemento desaparezca (por ejemplo, loaders).

Ejemplo típico antes de un click:

wait.until(ExpectedConditions.elementToBeClickable(loginButton)).click();

Esto elimina muchos errores clásicos de Selenium.

Otras condiciones útiles según el caso:

  • textToBePresentInElement
    Útil cuando: el elemento ya existe pero el contenido cambia dinámicamente
  • urlContains / urlToBe
    Muy común después de: login, navegación, redirecciones
  • titleContains
    Útil como validación simple de carga de página
  • frameToBeAvailableAndSwitchToIt
    Importante si alguna vez hay iframes
No es necesario memorizar todas las condiciones de ExpectedConditions.
En la práctica, la mayoría de los tests se resuelven con visibilidad, clickeabilidad, presencia e invisibilidad.
El resto se usa solo cuando el flujo lo pide.

Por qué WebDriverWait sí da control

Con espera explícita:

  • qué estoy esperando
  • por qué estoy esperando
  • dónde está la espera
  • puedo elegir distintas condiciones según el caso

Comparado con la implícita:

Implícita:
“Esperá si no existe.”
Explícita:
“Esperá hasta que esté listo para interactuar.”

Ventaja clave frente a la espera implícita

Con WebDriverWait:

  • no hay esperas globales invisibles
  • no se mezclan responsabilidades
  • el test expresa intención

El código deja de ser:

“Probá suerte y cruzá los dedos”

y pasa a ser:

“Esperá exactamente lo que necesito”.

Buena práctica en frameworks

En frameworks reales de Selenium:

  • se evita implicitWait
  • se evita Thread.sleep
  • se usan solo esperas explícitas
  • las esperas se encapsulan en:
    • Page Objects
    • Actions
    • Helpers

De esta forma, los tests quedan enfocados en el negocio, no en la sincronización.


Conclusión sobre WebDriverWait

WebDriverWait es el mecanismo central para lograr tests:

  • estables
  • predecibles
  • legibles
  • escalables
No espera tiempo.
Espera condiciones reales del DOM.

En la siguiente sección profundizo en FluentWait, que es una extensión más configurable de este mismo concepto.


Esperas explícitas avanzadas (Selenium): FluentWait

Después de entender WebDriverWait, FluentWait aparece casi naturalmente.

No es un tipo de espera distinto en concepto, sino una versión más configurable de la espera explícita.

La idea central es la misma:

Esperar una condición concreta durante un tiempo máximo.

La diferencia es que con FluentWait puedo controlar cómo se hace esa espera.


Qué agrega FluentWait respecto a WebDriverWait

FluentWait permite configurar explícitamente:

  • tiempo máximo de espera (timeout)
  • intervalo entre intentos (polling)
  • excepciones a ignorar mientras espera

En WebDriverWait, estos valores existen, pero vienen con defaults razonables.


Cómo se configura FluentWait

Partimos de esto:

Vista de configuración de FluentWait con withTimeout, pollingEvery e ignoring.
Wait<WebDriver> fwait = new FluentWait<>(driver)
        .withTimeout(Duration.ofSeconds(15))
        .pollingEvery(Duration.ofMillis(200))
        .ignoring(NoSuchElementException.class);

1️⃣ Wait<WebDriver> fwait = ...

¿Qué es Wait<WebDriver>?

  • Wait es una interfaz de Selenium (org.openqa.selenium.support.ui.Wait)
  • Representa “algo que sabe esperar una condición”

El <WebDriver> significa:

“La condición que voy a evaluar recibe un WebDriver”.

O sea: cada vez que FluentWait evalúe la condición, le pasa el driver.

📌 fwait no es una palabra reservada, es solo el nombre de la variable (como wait antes).

2️⃣ new FluentWait<>(driver)

Acá creás una espera explícita configurable.

Le pasás el driver porque:

  • FluentWait necesita algo sobre lo que evaluar condiciones
  • en Selenium, eso suele ser el WebDriver

Conceptualmente:

“Quiero una espera que evalúe condiciones usando este driver”.

3️⃣ .withTimeout(Duration.ofSeconds(15))

Esto define el tiempo máximo total de espera.

Significa:

“Si en 15 segundos la condición no se cumple, fallá”.

Importante:

  • No espera siempre 15 segundos
  • Termina apenas la condición se cumple
  • Si nunca se cumple → TimeoutException

Es el equivalente al timeout de WebDriverWait.

4️⃣ .pollingEvery(Duration.ofMillis(200))

Esta es una de las grandes diferencias con WebDriverWait.

Define cada cuánto FluentWait reintenta la condición.

En este caso:

  • cada 200 ms
  • evalúa la condición de nuevo

Ejemplo mental:

0 ms → intento
200 ms → intento
400 ms → intento
600 ms → intento
...

Por defecto:

  • WebDriverWait usa ~500 ms
  • con FluentWait vos lo decidís

5️⃣ .ignoring(NoSuchElementException.class)

Esto es fundamental.

Le decís a FluentWait:

“Si al evaluar la condición ocurre esta excepción, no falles.
Ignorala y seguí intentando.”

En Selenium:

  • findElement lanza NoSuchElementException
  • eso es normal mientras el elemento todavía no aparece

Sin este ignoring(...):

  • la primera vez que no encuentra el elemento
  • el wait falla inmediatamente

Con esto:

  • el error se ignora
  • FluentWait sigue esperando hasta que:
    • aparezca el elemento
    • o se agote el timeout

📌 Debe ser org.openqa.selenium.NoSuchElementException, no la de Java.

6️⃣ Qué es fwait después de todo esto

Después de esa configuración, fwait es:

Una espera explícita que:puede durar hasta 15 segundosreintenta cada 200 msignora errores transitorios de “elemento no encontrado”

Pero todavía no está esperando nada.

⚠️ Esto es importante:

  • FluentWait no hace nada por sí solo
  • Solo espera cuando llamás a fwait.until(...)

7️⃣ Cómo se completa el cuadro mental

Esta línea solo configura la estrategia de espera:

Vista de configuración de FluentWait con withTimeout, pollingEvery e ignoring.
Esta configuración no ejecuta ninguna espera por sí sola.
Define cómo se va a esperar: cuánto tiempo, cada cuánto reintentar y qué errores ignorar.
La espera real ocurre recién cuando se llama a until(...).

La espera real ocurre recién acá:

WebElement usuario = fwait.until(d -> d.findElement(usuarioInput));

o acá:

WebElement usuario =
fwait.until(ExpectedConditions.visibilityOfElementLocated(usuarioInput));

"Hay dos formas de uso, próxima sección se describen."

Ahí FluentWait:

  1. ejecuta la función
  2. si falla → espera 200 ms
  3. reintenta
  4. hasta que funcione o pasen 15 s

Las dos maneras de usar FluentWait.until(...)

🅰️ Forma A — condición propia (lambda)

Línea de código usando fwait.until con lambda: d -> d.findElement(usuarioInput)
Forma A: condición propia con lambda — control total sobre la lógica de espera.
WebElement usuario = fwait.until(d -> d.findElement(usuarioInput));

Qué está pasando acá

  • d es el WebDriver
  • vos definís tu propia condición. Podés usar cualquier lógica que puedas expresar como condición, siempre que devuelva algo cuando se cumple (o null/false cuando no).
  • Selenium:
    • ejecuta esa función
    • si falla → espera (polling)
    • reintenta hasta que funcione

👉 Esto es FluentWait en estado puro.

Cuándo usar esta forma

  • cuando necesitás lógica custom
  • cuando ExpectedConditions no alcanza
  • cuando querés controlar exactamente qué significa “listo”

Ejemplo más avanzado:

WebElement usuario =
    fwait.until(d -> {
        WebElement e = d.findElement(usuarioInput);
        return e.isDisplayed() && e.isEnabled() ? e : null;
    });

🅱️ Forma B — usando ExpectedConditions (como WebDriverWait)

Línea de código usando fwait.until con ExpectedConditions.visibilityOfElementLocated
Forma B: reutilizando ExpectedConditions — misma legibilidad que WebDriverWait, con configuración custom.
WebElement usuario =
    fwait.until(ExpectedConditions.visibilityOfElementLocated(usuarioInput));

Qué está pasando acá

  • usás condiciones ya armadas
  • FluentWait solo aporta:
    • timeout custom
    • polling custom
    • ignores custom
  • la condición sigue siendo la de Selenium

👉 Funciona igual que WebDriverWait, pero con más control.

Cuándo usar esta forma

  • cuando querés la legibilidad de ExpectedConditions
  • cuando solo necesitás ajustar polling / ignores
  • cuando no querés escribir lógica propia

Importante: NO es “usar WebDriverWait”

En la forma B:

  • no estás usando WebDriverWait
  • estás usando ExpectedConditions
  • con tu propio FluentWait

ExpectedConditions:

  • no depende de WebDriverWait
  • devuelve una condición reusable
  • puede ser evaluada por cualquier Wait

Esto es un detalle técnico fino, pero importante.


Comparación clara

FormaQué definísCuándo conviene
Lambda (d -> ...)Tu propia condiciónLógica especial
ExpectedConditionsCondición estándar90% de los casos
FluentWait puede evaluarse de dos maneras:
usando una condición propia (lambda) o reutilizando ExpectedConditions.
En ambos casos, FluentWait controla el tiempo, el polling y las excepciones ignoradas.

Qué hace realmente FluentWait

Internamente, FluentWait:

  • ejecuta la lógica de espera en ciclos
  • reintenta según el polling configurado
  • evalúa la condición en cada intento
  • termina apenas se cumple
  • lanza TimeoutException si el tiempo se agota

Conceptualmente, es el mismo mecanismo que WebDriverWait, pero sin valores implícitos.


Cuándo tiene sentido usar FluentWait

En la mayoría de los casos, WebDriverWait es suficiente.

FluentWait se vuelve útil cuando:

  • la UI es muy dinámica
  • hay loaders intermitentes
  • los elementos aparecen y desaparecen
  • hay renders parciales
  • necesitás polling más agresivo o más relajado

Ejemplo típico:

  • SPAs complejas
  • animaciones
  • grids que se recargan parcialmente

Relación con WebDriverWait

Una forma clara de pensarlo es esta:

  • WebDriverWait → espera explícita con defaults
  • FluentWait → espera explícita configurable

De hecho:

WebDriverWait está construido sobre FluentWait.

No compiten, se complementan.


Buenas prácticas

En frameworks reales:

  • ✔️ usar WebDriverWait como estándar
  • ✔️ recurrir a FluentWait solo cuando hay un motivo claro
  • ❌ no usar FluentWait “por las dudas”
  • ❌ no reemplazar todo por FluentWait

Más configuración no significa mejor test.


Conclusión sobre FluentWait

FluentWait no es la primera herramienta que se aprende,
pero sí la que te da control cuando el comportamiento por defecto no alcanza.

Es una herramienta de precisión, no de uso masivo.

Si WebDriverWait no resuelve el problema, recién ahí tiene sentido bajar a FluentWait.


Imports necesarios según el tipo de espera

Hasta acá hablé del comportamiento de cada tipo de espera.
Para cerrar, dejo los imports mínimos necesarios en Java para cada caso.

Imports de Java organizados por categorías: estructura del proyecto, Selenium core, Selenium waits, TestNG y Java Duration
Imports organizados: en un framework real, esta lista vive en Pages o Helpers, no en los tests.

Pausa fija (Java): Thread.sleep

// No requiere import

Thread.sleep pertenece a java.lang, que se importa automáticamente.

⚠️ Lo que sí aparece es la excepción:

throws InterruptedException o el manejo con try / catch.

Espera implícita (Selenium): implicitWait

import java.time.Duration;

La espera implícita se configura desde el WebDriver y solo requiere Duration.

Esperas explícitas (Selenium): WebDriverWait

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;
  • WebDriverWait: el mecanismo de espera
  • ExpectedConditions: condiciones reutilizables
  • WebElement: el resultado de la espera
  • Duration: timeout

Esperas explícitas avanzadas: FluentWait

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.support.ui.Wait;
import org.openqa.selenium.support.ui.FluentWait;
import java.time.Duration;

⚠️ Importante:

  • Usar org.openqa.selenium.NoSuchElementException
  • No confundir con java.util.NoSuchElementException

Nota importante sobre imports y diseño del framework

En un framework bien estructurado:

  • los imports de waits viven en:
    • Page Objects
    • Helpers
    • Actions
  • no en los tests

El objetivo es que el test no “sepa”:

  • cómo se espera
  • qué excepciones se ignoran
  • qué polling se usa

Solo debería expresar negocio.


Qué esperas son las recomendadas (regla simple)

  • 80% de tus casos: WebDriverWait + ExpectedConditions
  • Casos especiales: FluentWait
  • Evitar: Thread.sleep
  • Evitar mezclar: implicit wait + explicit wait

Cierre general del tema esperas

Con esto se completa el panorama:

  • Thread.sleep → pausa ciega
  • implicitWait → espera genérica y global
  • WebDriverWait → sincronización explícita y controlada
  • FluentWait → control fino cuando hace falta

🔗 Todo el código de esta serie está en: github.com/cesarbeassuarez/qa-automation-lab
📂 selenium-java

Temas conectados:

Sesión 4 de este lab y Sesión 5 de este lab de Selenium con java.