Selenium

Tutorial Selenium: Interacción con interfaz de usuario

Aprende interacción avanzada en Selenium con botones, enlaces, elementos interactivos, ventanas, navegación, manejo de alertas, acciones arrastrar y soltar, hover, doble clic y más.

Aprende Selenium GRATIS y certifícate

Clics en botones, enlaces y elementos interactivos

En Selenium WebDriver, la interacción con elementos interactivos como botones y enlaces es esencial para la automatización de pruebas. Para realizar clics en estos elementos, primero debemos localizarlos y luego utilizar el método apropiado para interactuar con ellos.

Para comenzar, utilizamos el método findElement para localizar el elemento en la página:

WebElement botonEnviar = driver.findElement(By.id("boton-enviar"));

En este ejemplo, hemos localizado un botón mediante su atributo id. Una vez que tenemos el elemento, podemos interactuar con él utilizando el método click():

botonEnviar.click();

El método click() simula un clic de usuario sobre el elemento, activando cualquier evento asociado en la página web.

Cuando trabajamos con enlaces, podemos localizarlos utilizando estrategias como By.linkText() o By.partialLinkText():

WebElement enlaceContacto = driver.findElement(By.linkText("Contacto"));
enlaceContacto.click();

Esto permite hacer clic en un enlace cuyo texto visible es "Contacto". Si el texto es largo o variable, By.partialLinkText() puede ser útil:

WebElement enlaceTerminos = driver.findElement(By.partialLinkText("Términos"));
enlaceTerminos.click();

Es importante asegurarse de que el elemento está visible y habilitado antes de interactuar con él. Si el elemento no es interactuable en el momento del clic, se puede producir una excepción ElementNotInteractableException. Para evitar esto, podemos utilizar esperas explícitas:

import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;

// ...

WebDriverWait espera = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement botonDescargar = espera.until(ExpectedConditions.elementToBeClickable(By.id("boton-descargar")));
botonDescargar.click();

Con esta técnica, el programa esperará hasta que el botón sea clicable antes de intentar hacer clic.

Cuando los elementos no tienen atributos fácilmente identificables, los selectores CSS o XPath pueden ser muy útiles:

WebElement botonIniciar = driver.findElement(By.cssSelector("button[class*='iniciar-sesion']"));
botonIniciar.click();

En este caso, estamos localizando un botón cuya clase contiene "iniciar-sesion".

A continuación, presentamos un ejemplo completo utilizando JUnit 5 en una prueba:

import org.junit.jupiter.api.*;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;

public class InteraccionElementosTest {

    WebDriver driver;

    @BeforeEach
    void setUp() {
        driver = new ChromeDriver();
        driver.get("https://www.ejemplo.com");
    }

    @Test
    void testClicEnBoton() {
        WebElement botonBuscar = driver.findElement(By.name("buscar"));
        botonBuscar.click();
        Assertions.assertTrue(driver.getTitle().contains("Resultados"));
    }

    @Test
    void testClicEnEnlace() {
        WebElement enlaceAyuda = driver.findElement(By.linkText("Ayuda"));
        enlaceAyuda.click();
        Assertions.assertEquals("https://www.ejemplo.com/ayuda", driver.getCurrentUrl());
    }

    @AfterEach
    void tearDown() {
        driver.quit();
    }
}

En este ejemplo, hemos creado dos pruebas: una que hace clic en un botón y otra en un enlace. Utilizamos diferentes estrategias de localización y realizamos aserciones para verificar el comportamiento esperado.

Para elementos dinámicos o que requieren interacción especial, como botones dentro de menús desplegables, podemos utilizar acciones avanzadas:

WebElement menuUsuario = driver.findElement(By.id("menu-usuario"));
menuUsuario.click();

WebElement opcionPerfil = driver.findElement(By.id("opcion-perfil"));
opcionPerfil.click();

Aquí, primero hacemos clic en el menú para desplegar las opciones y luego en la opción deseada.

Es recomendable manejar posibles excepciones y errores. Por ejemplo, si el elemento puede no estar presente, podemos verificar su existencia:

if (!driver.findElements(By.id("boton-existir")).isEmpty()) {
    WebElement botonExistir = driver.findElement(By.id("boton-existir"));
    botonExistir.click();
}

Con este enfoque, evitamos que nuestra prueba falle si el elemento no está en la página.

Además, para mejorar la mantenibilidad del código, podemos crear métodos auxiliares:

private void hacerClicEnElemento(By localizador) {
    WebElement elemento = driver.findElement(localizador);
    elemento.click();
}

Y utilizarlos en nuestras pruebas:

@Test
void testClicEnElementoPersonalizado() {
    hacerClicEnElemento(By.cssSelector(".clase-personalizada"));
    Assertions.assertTrue(driver.findElement(By.id("resultado")).isDisplayed());
}

De esta manera, reutilizamos código y mantenemos nuestras pruebas limpias y organizadas.

Al interactuar con elementos, también es importante considerar el estado de la página. Por ejemplo, si un elemento solo aparece después de cierta acción:

WebElement botonMostrar = driver.findElement(By.id("boton-mostrar"));
botonMostrar.click();

WebDriverWait espera = new WebDriverWait(driver, Duration.ofSeconds(5));
WebElement elementoOculto = espera.until(ExpectedConditions.visibilityOfElementLocated(By.id("elemento-oculto")));
elementoOculto.click();

Utilizando esperas explícitas, nos aseguramos de que el elemento esté visible antes de interactuar con él.

driver.findElements(By.tagName("button"))
      .stream()
      .filter(WebElement::isDisplayed)
      .forEach(WebElement::click);

En este ejemplo, hacemos clic en todos los botones visibles de la página, utilizando expresiones lambda y funcionalidades de streams.

En resumen, para dominar los clics en botones, enlaces y elementos interactivos:

  • Utiliza los métodos de localización adecuados.
  • Asegura que los elementos están en el estado correcto antes de interactuar.
  • Maneja las posibles excepciones y utiliza esperas cuando sea necesario.
  • Aprende a aprovechar las características avanzadas de Java para mejorar tu código.

Con estas prácticas, serás capaz de crear pruebas robustas y eficientes con Selenium y JUnit 5.

Navegación entre pantallas

La navegación entre pantallas es una parte fundamental en la automatización de pruebas con Selenium. Para controlar el flujo de trabajo y simular la interacción del usuario, es esencial manejar adecuadamente las operaciones de navegación del navegador.

Para navegar a una URL específica, utilizamos el método get() del WebDriver:

driver.get("https://www.ejemplo.com");

Este método carga la página web indicada y espera a que se complete la carga del documento.

Si necesitamos navegar a una nueva página mientras mantenemos el historial de navegación, podemos emplear el método navigate().to():

driver.navigate().to("https://www.otro-ejemplo.com");

Con navigate().to(), es posible utilizar una sintaxis más flexible al aceptar un objeto URL:

URL url = new URL("https://www.otro-ejemplo.com");
driver.navigate().to(url);

Para simular el botón atrás del navegador y regresar a la página anterior, utilizamos:

driver.navigate().back();

Del mismo modo, para avanzar en el historial si hemos retrocedido previamente:

driver.navigate().forward();

Refrescar la página actual es sencillo con:

driver.navigate().refresh();

Ejemplo práctico:

import org.junit.jupiter.api.*;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import java.net.URL;

public class NavegacionTest {

    WebDriver driver;

    @BeforeEach
    void inicializar() {
        driver = new ChromeDriver();
    }

    @Test
    void testNavegarEntrePaginas() throws Exception {
        driver.get("https://www.ejemplo.com");
        Assertions.assertEquals("Página de Inicio - Ejemplo", driver.getTitle());

        driver.navigate().to("https://www.ejemplo.com/contacto");
        Assertions.assertEquals("Contacto - Ejemplo", driver.getTitle());

        driver.navigate().back();
        Assertions.assertEquals("Página de Inicio - Ejemplo", driver.getTitle());

        driver.navigate().forward();
        Assertions.assertEquals("Contacto - Ejemplo", driver.getTitle());

        driver.navigate().refresh();
        // Verificamos que seguimos en la misma página tras el refresco
        Assertions.assertEquals("Contacto - Ejemplo", driver.getTitle());
    }

    @AfterEach
    void finalizar() {
        driver.quit();
    }
}

En este test, navegamos entre la página de inicio y la de contacto, verificando el título de cada página para asegurarnos de que la navegación se realiza correctamente.

Es importante considerar que ciertas páginas pueden tardar en cargar. Para asegurar que las aserciones se realicen cuando la página esté completamente cargada, podemos utilizar esperas explícitas:

import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;

// ...

@Test
void testEsperarCargaPagina() {
    driver.get("https://www.ejemplo.com");

    new WebDriverWait(driver, Duration.ofSeconds(10))
        .until(webDriver -> webDriver.getTitle().equals("Página de Inicio - Ejemplo"));

    Assertions.assertEquals("Página de Inicio - Ejemplo", driver.getTitle());
}

En este ejemplo, usamos una función lambda para esperar hasta que el título de la página sea el esperado.

Al navegar entre pantallas, es posible que se abran nuevas ventanas o pestañas. Para manejar múltiples ventanas, almacenamos los identificadores y cambiamos el contexto cuando sea necesario:

@Test
void testManejoMultiplesVentanas() {
    driver.get("https://www.ejemplo.com");
    String ventanaPrincipal = driver.getWindowHandle();

    // Supongamos que un enlace abre una nueva ventana
    driver.findElement(By.id("enlace-nueva-ventana")).click();

    var ventanas = driver.getWindowHandles();
    ventanas.stream()
            .filter(handle -> !handle.equals(ventanaPrincipal))
            .findFirst()
            .ifPresent(driver::switchTo().window);

    Assertions.assertEquals("Nueva Ventana - Ejemplo", driver.getTitle());

    // Volvemos a la ventana principal
    driver.switchTo().window(ventanaPrincipal);
    Assertions.assertEquals("Página de Inicio - Ejemplo", driver.getTitle());
}

Aquí, utilizamos streams para identificar la nueva ventana y cambiamos el foco del WebDriver a ella.

Cuando necesitamos abrir una serie de URLs y realizar acciones en cada una, podemos iterar utilizando estructuras funcionales:

@Test
void testNavegarVariasPaginas() {
    var urls = List.of(
        "https://www.ejemplo.com/pagina1",
        "https://www.ejemplo.com/pagina2",
        "https://www.ejemplo.com/pagina3"
    );

    urls.forEach(url -> {
        driver.navigate().to(url);
        // Realizar acciones en cada página
        Assertions.assertTrue(driver.getTitle().contains("Ejemplo"));
    });
}

Este enfoque facilita la iteración y mantiene el código limpio y legible.

Es esencial manejar las excepciones que pueden ocurrir durante la navegación, como TimeoutException o NoSuchElementException. Implementar bloques try-catch y utilizar mensajes descriptivos mejora la robustez de las pruebas:

@Test
void testNavegacionConExcepciones() {
    try {
        driver.navigate().to("https://www.ejemplo-inexistente.com");
        Assertions.fail("Debería haber lanzado una excepción");
    } catch (Exception e) {
        Assertions.assertTrue(e instanceof TimeoutException || e instanceof WebDriverException);
        // Registramos el error para análisis posterior
        System.err.println("Error al navegar: " + e.getMessage());
    }
}

Al automatizar la navegación, es buena práctica verificar el estado HTTP de las respuestas. Aunque Selenium no proporciona directamente esta funcionalidad, podemos integrar herramientas adicionales o utilizar solicitudes HTTP:

@Test
void testVerificarEstadoHttp() throws IOException {
    var url = new URL("https://www.ejemplo.com");
    var conexion = (HttpURLConnection) url.openConnection();
    conexion.setRequestMethod("HEAD");
    var codigoRespuesta = conexion.getResponseCode();

    Assertions.assertEquals(200, codigoRespuesta);
}

Esta técnica nos permite asegurarnos de que la URL es accesible antes de que el WebDriver intente cargarla.

Asimismo, para escenarios donde la navegación depende de condiciones dinámicas, podemos utilizar estructuras condicionales:

@Test
void testNavegacionCondicional() {
    driver.get("https://www.ejemplo.com");

    if (driver.findElements(By.id("banner-promocional")).size() > 0) {
        driver.findElement(By.id("cerrar-banner")).click();
    }

    driver.findElement(By.id("enlace-iniciar-sesion")).click();
    Assertions.assertEquals("Iniciar Sesión - Ejemplo", driver.getTitle());
}

De esta manera, manejamos elementos que pueden o no estar presentes, garantizando que la navegación fluya correctamente.

Para terminar, recordemos que mantener un código limpio y organizado facilita el mantenimiento y la escalabilidad de nuestras pruebas. Podemos delegar la navegación en métodos utilitarios:

private void navegarA(String url) {
    driver.navigate().to(url);
    new WebDriverWait(driver, Duration.ofSeconds(10))
        .until(ExpectedConditions.urlToBe(url));
}

// Uso en un test
@Test
void testNavegarConMetodoUtilitario() {
    navegarA("https://www.ejemplo.com/pagina1");
    Assertions.assertEquals("Página 1 - Ejemplo", driver.getTitle());
}

Al centralizar la lógica de navegación, evitamos la duplicación de código y facilitamos futuras actualizaciones.

Manejo de alertas, ventanas emergentes y frames

Al automatizar pruebas web con Selenium WebDriver, es común encontrarse con alertas, ventanas emergentes y frames que requieren un manejo especial. Estas estructuras pueden interrumpir el flujo de la prueba si no se controlan adecuadamente. A continuación, se detallan las técnicas para interactuar con estos elementos de manera efectiva.

Para gestionar alertas de JavaScript, Selenium ofrece la interfaz Alert. Cuando una alerta aparece en la página, es necesario cambiar el foco del controlador a dicha alerta. Esto se logra mediante:

Alert alerta = driver.switchTo().alert();

Una vez obtenida la referencia a la alerta, se pueden realizar operaciones como:

  • Aceptar la alerta:
  alerta.accept();
  • Cancelar la alerta (si es una confirmación):
  alerta.dismiss();
  • Obtener el texto de la alerta:
  String mensajeAlerta = alerta.getText();
  • Enviar texto a la alerta (en el caso de prompt):
  alerta.sendKeys("Texto de ejemplo");

Es importante recordar que hasta que no se interactúa con la alerta, no es posible continuar con otras acciones en la página principal.

Cuando se trabaja con ventanas emergentes o múltiples ventanas, es fundamental manejar los identificadores de ventana (window handles). El controlador puede cambiar el foco entre ventanas utilizando estos identificadores:

String ventanaPrincipal = driver.getWindowHandle();
Set<String> ventanasAbiertas = driver.getWindowHandles();

Para cambiar a una nueva ventana:

for (String ventana : ventanasAbiertas) {
    if (!ventana.equals(ventanaPrincipal)) {
        driver.switchTo().window(ventana);
        break;
    }
}

Una vez en la nueva ventana, se pueden realizar las acciones necesarias. Para regresar a la ventana original:

driver.close(); // Cierra la ventana actual
driver.switchTo().window(ventanaPrincipal); // Regresa a la ventana principal

El manejo de frames o iframes implica cambiar el contexto del controlador al frame específico antes de interactuar con los elementos dentro de él. Esto se puede hacer de varias maneras:

  • Por nombre o ID del frame:
  driver.switchTo().frame("nombreFrame");
  • Por índice:
  driver.switchTo().frame(0); // Cambia al primer frame
  • Por elemento WebElement:
  WebElement frameElemento = driver.findElement(By.tagName("iframe"));
  driver.switchTo().frame(frameElemento);

Después de interactuar con el contenido del frame, es necesario volver al contexto principal:

driver.switchTo().defaultContent();

A continuación, se presenta un ejemplo práctico utilizando JUnit 5 y aprovechando las características modernas de Java:

import org.junit.jupiter.api.*;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;

import java.time.Duration;

public class ManejoAlertasVentanasFramesTest {

    WebDriver driver;

    @BeforeEach
    void configurar() {
        driver = new ChromeDriver();
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        driver.get("https://www.ejemplo.com");
    }

    @Test
    void manejarAlerta() {
        WebElement botonAlerta = driver.findElement(By.id("boton-alerta"));
        botonAlerta.click();

        Alert alerta = driver.switchTo().alert();
        String textoAlerta = alerta.getText();
        Assertions.assertEquals("Esto es una alerta", textoAlerta);

        alerta.accept(); // Acepta la alerta
    }

    @Test
    void manejarVentanaEmergente() {
        String ventanaPrincipal = driver.getWindowHandle();
        WebElement enlaceVentana = driver.findElement(By.id("enlace-ventana"));
        enlaceVentana.click();

        driver.getWindowHandles()
              .stream()
              .filter(handle -> !handle.equals(ventanaPrincipal))
              .findFirst()
              .ifPresent(handle -> driver.switchTo().window(handle));

        Assertions.assertEquals("Nueva Ventana", driver.getTitle());

        driver.close(); // Cierra la ventana emergente
        driver.switchTo().window(ventanaPrincipal);
        Assertions.assertEquals("Página Principal", driver.getTitle());
    }

    @Test
    void manejarFrame() {
        driver.switchTo().frame("frameContenido");

        WebElement elementoEnFrame = driver.findElement(By.id("elemento-frame"));
        elementoEnFrame.click();

        driver.switchTo().defaultContent();

        WebElement elementoPrincipal = driver.findElement(By.id("elemento-principal"));
        Assertions.assertTrue(elementoPrincipal.isDisplayed());
    }

    @AfterEach
    void cerrar() {
        driver.quit();
    }
}

En el primer test, se trata una alerta, obteniendo su texto y aceptándola. En el segundo, se maneja una ventana emergente, cambiando el foco y verificando el título. En el tercero, se interactúa con un frame, cambiando al contexto del frame y luego regresando al principal.

Es esencial utilizar esperas cuando se trabaja con estos elementos para asegurar que estén disponibles antes de interactuar. Por ejemplo:

import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;

// ...

WebDriverWait espera = new WebDriverWait(driver, Duration.ofSeconds(10));
espera.until(ExpectedConditions.alertIsPresent());

Esta espera garantiza que la alerta esté presente antes de intentar acceder a ella.

Al manejar prompts que requieren entrada de texto:

WebElement botonPrompt = driver.findElement(By.id("boton-prompt"));
botonPrompt.click();

Alert alertaPrompt = driver.switchTo().alert();
alertaPrompt.sendKeys("Respuesta al prompt");
alertaPrompt.accept();

De esta forma, se puede enviar texto a la alerta y luego aceptarla.

Es común que las ventanas emergentes se abran debido a acciones del usuario. Para asegurarse de capturar el identificador de una nueva ventana:

String ventanaActual = driver.getWindowHandle();
Set<String> ventanasAntes = driver.getWindowHandles();

WebElement botonNuevaVentana = driver.findElement(By.id("boton-nueva-ventana"));
botonNuevaVentana.click();

Set<String> ventanasDespues = driver.getWindowHandles();
ventanasDespues.removeAll(ventanasAntes);

String nuevaVentana = ventanasDespues.iterator().next();
driver.switchTo().window(nuevaVentana);

Este enfoque garantiza que se obtenga el nuevo identificador de ventana después de la acción que la abre.

Para manejar múltiples frames anidados, se pueden encadenar los cambios de contexto:

driver.switchTo().frame("frameExterno");
driver.switchTo().frame("frameInterno");

WebElement elementoInterno = driver.findElement(By.id("elemento-interno"));
elementoInterno.click();

driver.switchTo().defaultContent(); // Regresa al contexto principal

Al trabajar con frames, es útil verificar su disponibilidad:

WebDriverWait espera = new WebDriverWait(driver, Duration.ofSeconds(10));
espera.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("frameContenido"));

Esto asegura que el frame esté listo antes de cambiar el contexto.

Es importante manejar posibles excepciones al trabajar con alertas y ventanas. Por ejemplo, al intentar cerrar una alerta que no existe:

try {
    Alert alerta = driver.switchTo().alert();
    alerta.dismiss();
} catch (NoAlertPresentException e) {
    // No hay alerta presente, continuar sin interrupciones
}

Este manejo evita que la prueba falle inesperadamente si la alerta no está presente.

Además, es recomendable utilizar funciones lambda y streams de Java para simplificar el código. Por ejemplo, para encontrar y cambiar a una ventana específica:

driver.getWindowHandles()
      .stream()
      .filter(handle -> !handle.equals(ventanaPrincipal))
      .findFirst()
      .ifPresent(driver.switchTo()::window);

Este enfoque funcional hace que el código sea más conciso y legible.

En resumen, el manejo adecuado de alertas, ventanas emergentes y frames es fundamental para asegurar que las pruebas automatizadas se ejecuten sin interrupciones. Al utilizar las herramientas y técnicas proporcionadas por Selenium WebDriver, se pueden controlar estos elementos de manera eficaz, garantizando la fiabilidad y robustez de las pruebas.

Acciones avanzadas: arrastrar, soltar, hover, doble clic

En Selenium WebDriver, las acciones avanzadas permiten simular interacciones más complejas del usuario, como arrastrar y soltar, pasar el ratón por encima de un elemento (hover) o realizar un doble clic. Estas operaciones se gestionan mediante la clase Actions, que proporciona una interfaz fluida para encadenar acciones y facilitar su uso.

Para utilizar la clase Actions, es necesario importarla y crear una instancia, pasando el WebDriver como parámetro:

import org.openqa.selenium.interactions.Actions;

// ...

Actions acciones = new Actions(driver);

Arrastrar y soltar

El arrastrar y soltar es común en interfaces de usuario que permiten mover elementos de un lugar a otro. Con Actions, podemos simular esta interacción de varias maneras.

Ejemplo de arrastrar y soltar por elementos:

WebElement fuente = driver.findElement(By.id("elemento-origen"));
WebElement destino = driver.findElement(By.id("elemento-destino"));

acciones.dragAndDrop(fuente, destino).perform();

En este caso, identificamos los elementos de origen y destino, y utilizamos dragAndDrop para realizar la operación. Es importante finalizar la secuencia con perform() para ejecutar las acciones.

Ejemplo de arrastrar y soltar por coordenadas:

acciones.clickAndHold(fuente)
        .moveByOffset(100, 0)
        .release()
        .perform();

Aquí, después de hacer clic y mantener en el elemento de origen, nos movemos una distancia específica usando moveByOffset, y luego soltamos el elemento con release().

Hover (pasar el ratón por encima)

La acción de hover es útil para interactuar con menús desplegables o elementos que reaccionan al pasar el ratón por encima. Para simular esta acción:

WebElement menu = driver.findElement(By.id("menu-principal"));

acciones.moveToElement(menu).perform();

Con moveToElement, posicionamos el cursor sobre el elemento indicado. Si necesita esperar a que aparezcan submenús u otros elementos, es recomendable utilizar esperas explícitas:

WebElement submenu = espera.until(ExpectedConditions.visibilityOfElementLocated(By.id("submenu-opcion")));
submenu.click();

De esta forma, aseguramos que el submenu es visible antes de interactuar con él.

Doble clic

Para realizar un doble clic sobre un elemento, utilizamos el método doubleClick:

WebElement boton = driver.findElement(By.id("boton-doble-clic"));

acciones.doubleClick(boton).perform();

Este método es útil cuando un elemento tiene un comportamiento diferente al ser doble clicado, como la selección de texto o la apertura de opciones avanzadas.

Contexto (clic derecho)

Para simular un clic derecho y abrir el menú contextual:

WebElement archivo = driver.findElement(By.id("archivo-documento"));

acciones.contextClick(archivo).perform();

Después de abrir el menú contextual, podemos navegar por las opciones utilizando acciones adicionales.

Acciones encadenadas

La clase Actions permite encadenar acciones para realizar interacciones más complejas. Por ejemplo, arrastrar un elemento y hacer doble clic en otro:

acciones.clickAndHold(fuente)
        .moveToElement(destino)
        .release()
        .doubleClick(destino)
        .perform();

Este flujo simula mantener pulsado en el elemento de origen, moverlo al elemento de destino, soltarlo y luego hacer doble clic en el destino.

Uso de funciones lambda y paradigma funcional

Con Java 23, podemos aprovechar las funciones lambda y el paradigma funcional para hacer el código más legible. Por ejemplo, si queremos realizar acciones sobre una lista de elementos:

List<WebElement> elementos = driver.findElements(By.className("opcion-menu"));

elementos.forEach(elemento -> acciones.moveToElement(elemento).perform());

En este caso, estamos pasando el ratón por encima de cada elemento del menú, uno tras otro.

Ejemplo práctico con JUnit 5

A continuación, se muestra un test completo que utiliza acciones avanzadas:

import org.junit.jupiter.api.*;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;

public class AccionesAvanzadasTest {

    WebDriver driver;
    WebDriverWait espera;
    Actions acciones;

    @BeforeEach
    void configurar() {
        driver = new ChromeDriver();
        espera = new WebDriverWait(driver, Duration.ofSeconds(10));
        acciones = new Actions(driver);
        driver.get("https://www.ejemplo.com");
    }

    @Test
    void testArrastrarYSoltar() {
        WebElement elementoOrigen = driver.findElement(By.id("drag-source"));
        WebElement elementoDestino = driver.findElement(By.id("drop-target"));

        acciones.dragAndDrop(elementoOrigen, elementoDestino).perform();

        String textoResultado = elementoDestino.getText();
        Assertions.assertEquals("Elemento soltado", textoResultado);
    }

    @Test
    void testHoverSobreElemento() {
        WebElement menuPrincipal = driver.findElement(By.id("menu-principal"));

        acciones.moveToElement(menuPrincipal).perform();

        WebElement submenu = espera.until(ExpectedConditions.visibilityOfElementLocated(By.id("submenu-opcion")));
        submenu.click();

        Assertions.assertEquals("https://www.ejemplo.com/opcion", driver.getCurrentUrl());
    }

    @Test
    void testDobleClicEnBoton() {
        WebElement boton = driver.findElement(By.id("boton-doble-clic"));

        acciones.doubleClick(boton).perform();

        WebElement mensaje = driver.findElement(By.id("mensaje-doble-clic"));
        Assertions.assertTrue(mensaje.isDisplayed());
    }

    @Test
    void testClicDerechoEnElemento() {
        WebElement elemento = driver.findElement(By.id("elemento-contextual"));

        acciones.contextClick(elemento).perform();

        WebElement opcionEliminar = espera.until(ExpectedConditions.visibilityOfElementLocated(By.id("opcion-eliminar")));
        opcionEliminar.click();

        WebElement confirmacion = driver.findElement(By.id("confirmacion-eliminado"));
        Assertions.assertTrue(confirmacion.isDisplayed());
    }

    @AfterEach
    void cerrar() {
        driver.quit();
    }
}

En este test:

  • Se realizan pruebas de arrastrar y soltar, verificando que el elemento ha sido soltado correctamente.
  • Se simula un hover sobre un menú y se hace clic en una opción que aparece al pasar el ratón.
  • Se ejecuta un doble clic en un botón y se comprueba que se muestra un mensaje como resultado.
  • Se efectúa un clic derecho en un elemento y se selecciona una opción del menú contextual.

Consideraciones sobre sincronización

Al trabajar con acciones avanzadas, es crucial manejar la sincronización para asegurar que los elementos están en el estado correcto antes de interactuar. Utilizar esperas explícitas ayuda a evitar excepciones como ElementNotInteractableException o StaleElementReferenceException.

Por ejemplo, esperar a que un elemento sea interactuable:

WebElement botonAccion = espera.until(ExpectedConditions.elementToBeClickable(By.id("boton-accion")));
acciones.doubleClick(botonAccion).perform();

De esta manera, garantizamos que el botón está listo para ser doble clicado.

Personalización de acciones

Las acciones pueden ser más detalladas utilizando métodos como moveByOffset para mover el ratón a coordenadas específicas, o keyDown y keyUp para simular pulsaciones de teclado en combinación con acciones del ratón.

Ejemplo de arrastrar y soltar con desplazamiento personalizado:

Point ubicacionOrigen = elementoOrigen.getLocation();
Point ubicacionDestino = elementoDestino.getLocation();
int offsetX = ubicacionDestino.getX() - ubicacionOrigen.getX();
int offsetY = ubicacionDestino.getY() - ubicacionOrigen.getY();

acciones.clickAndHold(elementoOrigen)
        .moveByOffset(offsetX, offsetY)
        .release()
        .perform();

Al calcular el desplazamiento entre los elementos, podemos arrastrar y soltar con precisión incluso en casos complejos.

Gestión de excepciones

Es recomendable manejar posibles excepciones durante las acciones avanzadas, especialmente en entornos dinámicos donde los elementos pueden cambiar de estado.

try {
    acciones.moveToElement(elementoInexistente).perform();
} catch (NoSuchElementException e) {
    System.err.println("El elemento no se encontró: " + e.getMessage());
}

Al capturar la excepción, podemos registrar el error y tomar decisiones informadas sin interrumpir el flujo de pruebas.

Aprovechamiento de Java funcional

Con las mejoras de programación funcional de Java, podemos utilizar métodos funcionales y flujos de datos para simplificar nuestras pruebas.

Aplicación de flujos en acciones:

driver.findElements(By.cssSelector(".items-arrastrables"))
      .stream()
      .forEach(item -> acciones.dragAndDrop(item, contenedorDestino).perform());

Este enfoque nos permite arrastrar y soltar una colección de elementos de forma concisa y elegante.

Aprende Selenium GRATIS online

Todas las lecciones de Selenium

Accede a todas las lecciones de Selenium y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Selenium y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Clics en botones, enlaces y elementos interactivos
  • Navegación entre pantallas
  • Manejo de alertas, ventanas emergentes y frames
  • Acciones avanzadas: arrastrar, soltar, hover, doble clic