Selenium

Tutorial Selenium: Localización de elementos web

Aprende a localizar elementos en el DOM con mediante diferentes tipos de selectores nombre, id, etiquetas, css, xpath en Selenium para pruebas de software.

Aprende Selenium GRATIS y certifícate

Selectores básicos: id, nombre, clase, etiquetas, texto de links

Para interactuar con elementos en una página web usando Selenium, es esencial poder localizarlos de manera precisa. Los selectores básicos nos permiten identificar elementos de forma sencilla utilizando atributos comunes presentes en los elementos HTML.

El id es un atributo único que identifica de manera inequívoca a un elemento dentro del DOM. Es el método más rápido y fiable para localizar un elemento en una página web.

@Test
public void testBusquedaPorId() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    WebElement botonEnviar = driver.findElement(By.id("boton-enviar"));
    botonEnviar.click();
    
    driver.quit();
}

El atributo name se utiliza frecuentemente en formularios para identificar campos de entrada. Aunque puede haber múltiples elementos con el mismo name, en contextos como formularios suele ser suficiente para una identificación correcta.

@Test
public void testBusquedaPorNombre() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    WebElement campoCorreo = driver.findElement(By.name("correo"));
    campoCorreo.sendKeys("usuario@ejemplo.com");
    
    driver.quit();
}

La clase o class es un atributo que puede compartir múltiples elementos. Se utiliza para aplicar estilos CSS, pero también es útil para localizar elementos. Cuando se utiliza By.className(), se debe proporcionar el nombre exacto de la clase.

@Test
public void testBusquedaPorClase() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    WebElement elementosDestacados = driver.findElement(By.className("destacado"));
    elementosDestacados.click();
    
    driver.quit();
}

Las etiquetas o tags son los nombres de los elementos HTML, como div, input, button, etc. Con By.tagName() podemos encontrar elementos basándonos en su etiqueta.

@Test
public void testBusquedaPorEtiqueta() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    List<WebElement> enlaces = driver.findElements(By.tagName("a"));
    enlaces.get(0).click();
    
    driver.quit();
}

El texto de enlaces es útil para localizar links en una página. Con By.linkText() podemos encontrar un enlace cuyo texto coincida exactamente con el proporcionado.

@Test
public void testBusquedaPorTextoEnlace() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    WebElement enlaceContacto = driver.findElement(By.linkText("Contacto"));
    enlaceContacto.click();
    
    driver.quit();
}

Si el texto del enlace es largo o variable, podemos utilizar By.partialLinkText() para localizar enlaces que contengan una parte del texto.

@Test
public void testBusquedaPorTextoParcialEnlace() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    WebElement enlaceMasInfo = driver.findElement(By.partialLinkText("Más información"));
    enlaceMasInfo.click();
    
    driver.quit();
}

Es importante seleccionar el selector más apropiado para cada caso, priorizando aquellos que sean únicos y reduzcan la posibilidad de errores. Utilizar id y name es preferible cuando están disponibles y son únicos en el contexto de la página.

Al combinar estos selectores básicos, podemos navegar y automatizar interacciones en páginas web de manera eficiente con Selenium.

Selectores avanzados: css

Los selectores CSS avanzados permiten localizar elementos web de manera más precisa y flexible en páginas con estructuras HTML complejas. Con un buen dominio de estos selectores, podemos crear ubicaciones robustas que se adapten a los cambios en el DOM.

Selectores de atributos

Podemos seleccionar elementos basándonos en los atributos y sus valores. Por ejemplo, para seleccionar un elemento por su atributo data-test:

@Test
public void testSelectorAtributo() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector("[data-test='valor']"));
    elemento.click();
    
    driver.quit();
}

Selectores por múltiples clases

Cuando un elemento tiene varias clases, podemos seleccionarlo especificando todas ellas. Es importante usar el punto (.) antes de cada nombre de clase.

@Test
public void testSelectorMultiplesClases() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector(".clase1.clase2"));
    elemento.click();
    
    driver.quit();
}

Selectores de descendencia y jerarquía

Los combinadores nos permiten seleccionar elementos basados en su relación con otros elementos en el DOM.

  • Selector de descendientes: selecciona elementos que son descendientes de un ancestro específico.
@Test
public void testSelectorDescendiente() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector("div.contenedor p.parrafo"));
    elemento.click();
    
    driver.quit();
}

En el ejemplo anterior, seleccionamos todos los <p> con clase parrafo que son descendientes de un <div> con clase contenedor.

Selectores de hijos directos

Para seleccionar elementos que son hijos directos de un elemento padre, utilizamos el símbolo >.

@Test
public void testSelectorHijoDirecto() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector("ul.lista > li.item"));
    elemento.click();
    
    driver.quit();
}

Aquí, seleccionamos los <li> con clase item que son hijos directos de un <ul> con clase lista.

Selectores de hermanos adyacentes

El combinador de hermanos adyacentes (+) permite seleccionar un elemento que es inmediatamente posterior a otro elemento en el mismo nivel jerárquico.

@Test
public void testSelectorHermanoAdyacente() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector("h2.titulo + p.introduccion"));
    elemento.click();
    
    driver.quit();
}

En este caso, seleccionamos el <p> con clase introduccion que sigue directamente a un <h2> con clase titulo.

Selectores de hermanos generales

El combinador de hermanos generales (~) selecciona todos los elementos que comparten el mismo padre y siguen al elemento anterior.

@Test
public void testSelectorHermanosGenerales() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elementos = driver.findElements(By.cssSelector("h2.titulo ~ p"));
    elementos.forEach(WebElement::click);
    
    driver.quit();
}

Seleccionamos todos los <p> que siguen a un <h2> con clase titulo, sin importar si son inmediatamente después o no.

Pseudoclases estructurales

Las pseudoclases nos permiten seleccionar elementos basados en su posición dentro del DOM.

  • nth-child(n): selecciona el enésimo hijo de su padre.
@Test
public void testSelectorNthChild() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector("ul.lista > li:nth-child(2)"));
    elemento.click();
    
    driver.quit();
}

En este ejemplo, seleccionamos el segundo <li> hijo directo de <ul> con clase lista.

  • nth-of-type(n): selecciona el enésimo elemento de su tipo entre sus hermanos.
@Test
public void testSelectorNthOfType() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector("div.contenedor > p:nth-of-type(3)"));
    elemento.click();
    
    driver.quit();
}

Aquí, seleccionamos el tercer <p> entre los hijos <p> de <div> con clase contenedor.

Selectores de pseudoclases avanzados

  • first-child y last-child: seleccionan el primer y último hijo, respectivamente.
@Test
public void testSelectorFirstChild() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var primerElemento = driver.findElement(By.cssSelector("ul.lista > li:first-child"));
    primerElemento.click();
    
    driver.quit();
}
  • only-child: selecciona un elemento que es el único hijo de su padre.
@Test
public void testSelectorOnlyChild() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var unicoElemento = driver.findElement(By.cssSelector("div.contenedor > p:only-child"));
    unicoElemento.click();
    
    driver.quit();
}

Selectores de coincidencia de atributos

Podemos utilizar operadores para realizar coincidencias parciales de atributos.

  • Empieza con (**^=**):
@Test
public void testSelectorEmpiezaCon() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector("input[name^='usuario']"));
    elemento.sendKeys("miUsuario");
    
    driver.quit();
}
  • Termina con (**$=**):
@Test
public void testSelectorTerminaCon() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector("input[name$='email']"));
    elemento.sendKeys("correo@ejemplo.com");
    
    driver.quit();
}
  • Contiene (***=**):
@Test
public void testSelectorContiene() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector("input[name*='pass']"));
    elemento.sendKeys("miContraseña");
    
    driver.quit();
}

Combinación de selectores

La combinación de selectores nos permite crear búsquedas más precisas.

@Test
public void testSelectorCombinado() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elemento = driver.findElement(By.cssSelector("form#registro input[type='text'].campo"));
    elemento.sendKeys("valor");
    
    driver.quit();
}

En este ejemplo, seleccionamos un <input> de tipo text con clase campo que está dentro de un <form> con id registro.

Selección múltiple y operaciones funcionales

Podemos seleccionar múltiples elementos y utilizar el API de Streams de Java para operaciones funcionales.

@Test
public void testSelectorMultipleConStreams() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");
    
    var elementos = driver.findElements(By.cssSelector("ul.lista > li.item"));
    elementos.stream()
        .filter(e -> e.getText().contains("Oferta"))
        .forEach(WebElement::click);
    
    driver.quit();
}

En este ejemplo, seleccionamos todos los <li> con clase item dentro de <ul> con clase lista, filtramos aquellos cuyo texto contiene "Oferta" y hacemos clic en cada uno.

Buenas prácticas al usar selectores CSS

  • Priorizar selectores únicos y estables que no dependan de la estructura del DOM que pueda cambiar con frecuencia.
  • Evitar selectores excesivamente largos o complejos que sean difíciles de mantener.
  • Siempre que sea posible, utilizar atributos como id o data-test que sean específicos para pruebas automatizadas.

Al dominar los selectores avanzados CSS, optimizamos la robustez y mantenibilidad de nuestras pruebas automatizadas con Selenium.

Selectores avanzados: introducción a xpath

En Selenium, el uso de XPath es esencial para localizar elementos web que no pueden identificarse fácilmente mediante selectores básicos. XPath es un lenguaje de consultas que permite navegar por la estructura del DOM (Modelo de Objetos del Documento) de una página web, proporcionando una forma flexible y potente de dirigirse a elementos específicos.

XPath utiliza una sintaxis que refleja la jerarquía de elementos en una página HTML. Mediante expresiones XPath, podemos seleccionar nodos basándonos en sus relaciones estructurales, atributos y valores de texto.

Por ejemplo, para encontrar un elemento <input> cuyo atributo type sea "email", podemos utilizar la siguiente expresión XPath:

@Test
public void testBusquedaPorXpath() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    WebElement campoEmail = driver.findElement(By.xpath("//input[@type='email']"));
    campoEmail.sendKeys("usuario@ejemplo.com");

    driver.quit();
}

En este ejemplo, //input[@type='email'] selecciona cualquier elemento <input> en cualquier lugar del documento que tenga un atributo type con valor "email".

La doble barra // indica una búsqueda en todo el documento, y los corchetes [@atributo='valor'] permiten filtrar elementos basados en atributos específicos.

Las expresiones XPath también pueden utilizarse para navegar por relaciones de parentesco entre elementos. Por ejemplo, para seleccionar el elemento hijo <span> de un <div> con clase "mensaje":

@Test
public void testSeleccionarElementoHijo() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    WebElement mensaje = driver.findElement(By.xpath("//div[@class='mensaje']/span"));
    String texto = mensaje.getText();

    driver.quit();
}

Aquí, la expresión //div[@class='mensaje']/span selecciona el elemento <span> que es hijo directo de un <div> con clase "mensaje".

Las funciones de XPath permiten crear consultas más sofisticadas. Por ejemplo, para encontrar un elemento cuyo texto contenga una determinada cadena, podemos usar contains():

@Test
public void testBusquedaPorTextoParcial() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    WebElement enlace = driver.findElement(By.xpath("//a[contains(text(), 'Más información')]"));
    enlace.click();

    driver.quit();
}

En este caso, estamos localizando un enlace <a> cuyo texto contenga la cadena "Más información".

XPath también soporta el uso de operadores lógicos y funciones numéricas. Para seleccionar el tercer elemento de una lista:

@Test
public void testSeleccionarElementoPorIndice() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    WebElement tercerElemento = driver.findElement(By.xpath("(//ul[@id='lista-items']/li)[3]"));
    tercerElemento.click();

    driver.quit();
}

La expresión (//ul[@id='lista-items']/li)[3] selecciona el tercer <li> dentro del <ul> con id "lista-items".

Para mejorar la legibilidad y mantenimiento de las pruebas, es recomendable almacenar las expresiones XPath en variables descriptivas:

@Test
public void testUsoVariablesParaXpath() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    String xpathBotonEnviar = "//button[@type='submit']";
    WebElement botonEnviar = driver.findElement(By.xpath(xpathBotonEnviar));
    botonEnviar.click();

    driver.quit();
}

Las expresiones XPath relativas permiten comenzar la búsqueda desde un elemento conocido, lo cual es útil para elementos dinámicos:

@Test
public void testBusquedaRelativa() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    WebElement seccion = driver.findElement(By.id("seccion-articulos"));
    WebElement primerArticulo = seccion.findElement(By.xpath(".//h2"));
    String titulo = primerArticulo.getText();

    driver.quit();
}

El punto . al inicio de la expresión XPath indica que la búsqueda comienza desde el elemento actual seccion.

Para seleccionar elementos basados en atributos que comienzan o terminan con ciertos valores, podemos usar las funciones starts-with() y ends-with():

@Test
public void testBusquedaPorInicioAtributo() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    WebElement elemento = driver.findElement(By.xpath("//input[starts-with(@name, 'user')]"));
    elemento.sendKeys("nombreUsuario");

    driver.quit();
}

En este ejemplo, se selecciona un <input> cuyo atributo name comienza con "user".

Con XPath, también podemos utilizar operadores condicionales para combinar criterios:

@Test
public void testBusquedaConMultiplesCondiciones() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    WebElement elemento = driver.findElement(By.xpath("//div[@class='producto' and @data-disponible='true']"));
    elemento.click();

    driver.quit();
}

Aquí, el elemento <div> debe tener la clase "producto" y el atributo data-disponible con valor "true".

El uso de ejes XPath nos permite navegar por las relaciones entre nodos, como padre, hijo, hermano, etc. Por ejemplo, para seleccionar el elemento hermano siguiente:

@Test
public void testSeleccionarHermanoSiguiente() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    WebElement elementoActual = driver.findElement(By.id("actual"));
    WebElement elementoSiguiente = elementoActual.findElement(By.xpath("following-sibling::*[1]"));
    elementoSiguiente.click();

    driver.quit();
}

La expresión following-sibling::*[1] selecciona el primer hermano siguiente del elemento actual.

Con Java 23 y el paradigma funcional, podemos aprovechar streaming y lambda expressions para manejar listas de elementos:

@Test
public void testManejoListaElementos() {
    WebDriver driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    List<WebElement> elementos = driver.findElements(By.xpath("//ul[@class='menu']//li"));
    elementos.stream()
        .filter(elemento -> elemento.getText().contains("Oferta"))
        .findFirst()
        .ifPresent(WebElement::click);

    driver.quit();
}

En este ejemplo, encontramos todos los elementos <li> dentro de un <ul> con clase "menu", filtramos aquellos que contienen la palabra "Oferta" y hacemos clic en el primero que encontramos.

Es importante tener en cuenta las buenas prácticas al utilizar XPath:

  • Evitar rutas absolutas (como /html/body/div), ya que son frágiles ante cambios en la estructura de la página.
  • Utilizar atributos únicos siempre que sea posible para crear expresiones más robustas.
  • Priorizar la legibilidad y el mantenimiento de las expresiones XPath.

XPath es una herramienta poderosa para la automatización de pruebas con Selenium, y su dominio permite manejar incluso las interfaces web más complejas.

Herramientas para inspeccionar elementos

Para automatizar pruebas con Selenium de forma efectiva, es fundamental dominar las herramientas de inspección que ofrecen los navegadores modernos. Estas herramientas nos permiten explorar el DOM (Document Object Model) de una página web, identificar elementos y obtener sus selectores para interactuar con ellos en nuestros tests.

Los navegadores como Google Chrome y Mozilla Firefox incorporan potentes herramientas de desarrollo. Para acceder a ellas, podemos hacer clic derecho en cualquier parte de la página y seleccionar "Inspeccionar" o pulsar la tecla F12. Esto abre una interfaz donde podemos visualizar y analizar la estructura HTML y CSS de la página.

En el panel de elementos, al situar el cursor sobre el código HTML, el navegador resalta el elemento correspondiente en la página. Esta función es muy útil para identificar la ubicación exacta de un elemento. Además, podemos utilizar el selector de elementos (un icono en forma de cursor o lupa) para seleccionar elementos directamente en la página y ver su código asociado.

Una vez identificado el elemento, es posible acceder a sus atributos, como id, class, name y otros atributos personalizados. Estos atributos son esenciales para construir selectores únicos que utilizaremos en nuestros scripts de Selenium.

Por ejemplo, en Chrome, al hacer clic derecho sobre un elemento en el panel de elementos, podemos copiar sus selectores:

  • Copiar selector CSS: proporciona un selector CSS preciso para el elemento.
  • Copiar XPath completo: genera una ruta XPath absoluta del elemento.

Sin embargo, es recomendable revisar y, si es necesario, ajustar estos selectores para garantizar su robustez y evitar dependencias innecesarias de la estructura del DOM.

La consola del navegador es otra herramienta valiosa. Podemos probar nuestros selectores ejecutando comandos como:

document.querySelector("selector-css")

o

$x("expresión-xpath")

Esto nos permite verificar si el selector localiza correctamente el elemento deseado antes de implementarlo en nuestros tests.

Además de las herramientas integradas en los navegadores, existen extensiones que facilitan la generación y validación de selectores:

  • Selector Gadget: una extensión que ayuda a construir selectores CSS de manera intuitiva.
  • XPath Helper: permite evaluar expresiones XPath en tiempo real.
  • Selenium IDE: aunque es una herramienta de automatización, también puede utilizarse para identificar elementos y generar código.

Es importante destacar que, al inspeccionar elementos, debemos prestar atención a los atributos únicos que puedan servir como selectores fiables. Atributos como data-test-id o aria-label suelen ser utilizados específicamente para pruebas y pueden ofrecer mayor estabilidad frente a cambios en el diseño.

Un ejemplo práctico en un test de JUnit 5 sería:

@Test
void testAccesoPaginaPerfil() {
    var driver = new ChromeDriver();
    driver.get("https://miaplicacion.com");

    var botonPerfil = driver.findElement(By.cssSelector("button[data-test-id='boton-perfil']"));
    botonPerfil.click();

    driver.quit();
}

En este código, hemos utilizado el atributo data-test-id identificado mediante las herramientas de inspección para localizar el botón de perfil.

Las herramientas de accesibilidad integradas en los navegadores también son útiles. Nos permiten ver cómo los lectores de pantalla interpretan la página y descubrir atributos como role, aria-label o aria-labelledby, que pueden ser empleados como selectores en Selenium.

Por ejemplo:

@Test
void testAbrirMenuNavegacion() {
    var driver = new ChromeDriver();
    driver.get("https://miaplicacion.com");

    var botonMenu = driver.findElement(By.xpath("//button[@aria-label='Abrir menú de navegación']"));
    botonMenu.click();

    driver.quit();
}

Aquí, utilizamos el atributo aria-label para localizar el botón que abre el menú de navegación.

Cuando trabajamos con aplicaciones que cargan contenido dinámicamente, la pestaña de red en las herramientas de desarrollo nos permite:

  • Monitorizar las peticiones HTTP realizadas.
  • Identificar si se están realizando llamadas AJAX que puedan afectar a la disponibilidad de los elementos.
  • Verificar los tiempos de carga, lo cual es útil para implementar esperas en nuestros tests.

Asimismo, la pestaña de consola registra errores y advertencias en tiempo real. Si un elemento no se carga correctamente o hay errores en scripts que afectan al DOM, podremos detectarlo y ajustar nuestro test en consecuencia.

Otro recurso valioso es la capacidad de los navegadores para editar el DOM en tiempo real. Podemos modificar atributos, añadir o eliminar clases, y ver instantáneamente cómo afecta esto a la página. Esto es útil para simular diferentes estados de la aplicación y anticipar posibles escenarios en nuestros tests.

Por ejemplo, para probar cómo se comporta un formulario cuando un campo es inválido, podemos:

  • Editar el valor de un atributo class para añadir una clase de error.
  • Modificar directamente el valor de un campo de entrada.

Finalmente, es recomendable familiarizarse con los atajos de teclado de las herramientas de desarrollo para agilizar el proceso de inspección. Algunos atajos útiles en Chrome son:

  • Ctrl + Shift + C: activa el selector de elementos.
  • Ctrl + F: abre la búsqueda dentro del panel de elementos, permitiendo buscar selectores o texto específico.

Al aprovechar al máximo las herramientas de inspección, optimizamos el proceso de creación de selectores eficientes y reducimos el tiempo dedicado a la resolución de problemas en nuestros tests de Selenium.

Buenas prácticas en localización de elementos

Al automatizar pruebas con Selenium, una de las claves para lograr tests robustos y mantenibles es la correcta localización de los elementos web. Aplicar buenas prácticas en este aspecto minimiza problemas ante cambios en la interfaz y facilita el mantenimiento del código a largo plazo.

Es fundamental priorizar selectores que sean únicos y estables en el tiempo. Siempre que sea posible, utiliza el atributo id, ya que debería ser único en el DOM y proporciona una manera directa de identificar un elemento:

@Test
void testUsoDeIdUnico() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    var botonLogin = driver.findElement(By.id("boton-login"));
    botonLogin.click();

    driver.quit();
}

En caso de que el atributo id no esté disponible o no sea único, el siguiente paso es buscar atributos significativos como name o clases que identifiquen claramente al elemento:

@Test
void testUsoDeClaseDescriptiva() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    var enlaceRegistro = driver.findElement(By.className("enlace-registro"));
    enlaceRegistro.click();

    driver.quit();
}

Es recomendable evitar selectores que dependan de la estructura del DOM, como rutas XPath muy específicas, ya que son propensos a fallar si la estructura de la página cambia:

@Test
void testEvitarXPathFragil() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    // Evitar este tipo de selectores
    var elemento = driver.findElement(By.xpath("/html/body/div[2]/div[1]/ul/li[3]/a"));
    elemento.click();

    driver.quit();
}

En lugar de ello, utiliza selectores relativos y basados en atributos estables:

@Test
void testUsoDeSelectorRobusto() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    // Selector más robusto
    var elemento = driver.findElement(By.xpath("//ul[@id='menu-principal']/li/a[text()='Contacto']"));
    elemento.click();

    driver.quit();
}

La utilización de atributos personalizados como data-testid o aria-label puede mejorar la fiabilidad de los selectores. Estos atributos suelen ser ignorados por los cambios de estilo y permanecen constantes:

@Test
void testUsoDeAtributoPersonalizado() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    var botonBuscar = driver.findElement(By.cssSelector("button[data-testid='boton-buscar']"));
    botonBuscar.click();

    driver.quit();
}

Cuando trabajes con clases, ten en cuenta que un elemento puede tener múltiples clases. Es importante ser específico pero no excesivamente restrictivo:

@Test
void testSelectorPorMultiplesClases() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    var mensajeError = driver.findElement(By.cssSelector(".alerta.error"));
    String textoError = mensajeError.getText();

    driver.quit();
}

Para mejorar la legibilidad y mantenimiento del código, es aconsejable separar la lógica de localización en métodos o variables descriptivas:

@Test
void testUsoDeMetodosParaLocalizacion() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    var campoUsuario = obtenerCampoUsuario(driver);
    campoUsuario.sendKeys("miUsuario");

    driver.quit();
}

private WebElement obtenerCampoUsuario(WebDriver driver) {
    return driver.findElement(By.name("usuario"));
}

Al manejar elementos dinámicos o que aparecen tras ciertas acciones, es esencial implementar esperas explícitas para asegurar que el elemento esté presente antes de interactuar con él:

@Test
void testUsoDeEsperaExplicita() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    var wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    var botonCargarMas = wait.until(driver -> driver.findElement(By.id("cargar-mas")));
    botonCargarMas.click();

    driver.quit();
}

Evita el uso excesivo de selectores compuestos o demasiado genéricos que puedan coincidir con múltiples elementos. Esto puede provocar interacciones inesperadas y dificultar el debug:

@Test
void testEvitarSelectoresDemasiadoGenericos() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    // Este selector podría coincidir con varios elementos
    var botones = driver.findElements(By.tagName("button"));
    botones.get(0).click();

    driver.quit();
}

En su lugar, busca especificar más el selector para apuntar al elemento deseado:

@Test
void testSelectorEspecificoParaBoton() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    var botonEnviar = driver.findElement(By.cssSelector("form#contacto button[type='submit']"));
    botonEnviar.click();

    driver.quit();
}

Implementar el patrón de diseño Page Object Model (POM) puede ser beneficioso para organizar y reutilizar los selectores. Aunque su implementación detallada se aborda en otra sección, es importante reconocer su valor para las buenas prácticas.

Cuando las páginas contienen elementos anidados o componentes reutilizables, considera la posibilidad de encapsular la localización dentro de esos contextos para mejorar la modularidad del código:

@Test
void testLocalizacionDentroDeElementoPadre() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    var seccionNoticias = driver.findElement(By.id("seccion-noticias"));
    var titular = seccionNoticias.findElement(By.tagName("h2"));
    String textoTitular = titular.getText();

    driver.quit();
}

Para mantener tus tests resistentes a cambios en el frontend, colabora con el equipo de desarrollo para que incluyan atributos específicos para testing. Esto puede facilitar enormemente la localización de elementos:

@Test
void testColaboracionConDesarrolladores() {
    var driver = new ChromeDriver();
    driver.get("https://ejemplo.com");

    var campoBusqueda = driver.findElement(By.cssSelector("input[data-test='campo-busqueda']"));
    campoBusqueda.sendKeys("Selenium");

    driver.quit();
}

Finalmente, revisa y actualiza regularmente tus selectores. Las aplicaciones web están en constante evolución, y mantener los selectores actualizados es esencial para asegurar la fiabilidad de tus pruebas.

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

  • Aprender qué son los selectores
  • Usar selectores de id, nombre, clase y etiqueta
  • Usar selectores de css y xpath
  • Aprender la sintaxis xpath