Selenium
Tutorial Selenium: Dominio de selectores XPath y CSS
Aprende la sintaxis XPath para localización de elementos en el DOM y el uso de selectores CSS en Selenium para encontrar elementos.
Aprende Selenium GRATIS y certifícateDominio de selectores XPath y CSS
Para automatizar pruebas web de manera eficaz con Selenium, es fundamental dominar los selectores XPath y CSS. Estos selectores permiten localizar elementos en el DOM de una página web de forma precisa y fiable. Una adecuada comprensión de cómo funcionan facilita la creación de scripts más robustos y mantenibles.
Los selectores XPath y CSS son herramientas poderosas para navegar y seleccionar nodos en el documento HTML. Mientras que los selectores CSS son más concisos y se basan en las reglas de estilo utilizadas en el desarrollo web, los selectores XPath ofrecen una mayor flexibilidad al permitir navegaciones más complejas en el árbol DOM.
Es importante elegir el tipo de selector adecuado en función del contexto. Los selectores CSS suelen ser más rápidos y legibles, siendo ideales para localizar elementos con atributos únicos como clases o identificadores. Por otro lado, los selectores XPath son útiles cuando se necesita acceder a elementos basados en relaciones hierárquicas o condiciones más complejas.
A continuación, se muestra un ejemplo de cómo utilizar selectores XPath y CSS en pruebas con JUnit 5 y Selenium 4:
@Test
void testBusquedaConXPathYCSS() {
WebDriver driver = new ChromeDriver();
driver.get("https://www.ejemplo.com");
WebElement elementoConCSS = driver.findElement(By.cssSelector(".clase-ejemplo"));
elementoConCSS.click();
WebElement elementoConXPath = driver.findElement(By.xpath("//div[@id='contenido']//a[text()='Enlace']"));
elementoConXPath.click();
driver.quit();
}
En el ejemplo anterior, se utiliza By.cssSelector
para localizar un elemento por su clase y By.xpath
para encontrar un enlace específico dentro de un contenedor. Es esencial asegurarse de que los selectores sean lo más simples y eficientes posible para optimizar el rendimiento de las pruebas.
Al crear selectores, se recomienda evitar dependencias en atributos o estructuras que puedan cambiar con frecuencia. En su lugar, es preferible basarse en atributos estables o añadir identificadores únicos en el código fuente de la aplicación cuando sea posible.
El uso adecuado de los selectores también influye en la mantenibilidad del código. Selectores claros y bien diseñados facilitan la actualización de las pruebas cuando la interfaz de usuario cambia. Además, es conveniente agrupar la lógica de localización de elementos para reutilizar selectores comunes y reducir la duplicación de código.
Aplicar prácticas como la verificación de la unicidad de los selectores ayuda a prevenir errores en las pruebas. Antes de implementar un selector, es aconsejable probarlo en las herramientas de desarrollo del navegador para asegurarse de que devuelve el elemento esperado.
Finalmente, es importante mantenerse actualizado con las últimas novedades de Selenium y las mejoras en los selectores. Las versiones más recientes de Selenium ofrecen un mejor soporte y rendimiento al trabajar con selectores modernos.
Sintaxis básica de XPath
El XPath (XML Path Language) es un lenguaje que permite navegar por la estructura de un documento XML o HTML para localizar elementos de manera precisa. En el contexto de Selenium, XPath es una herramienta esencial para identificar y manipular elementos en una página web cuando otros selectores no son suficientes.
Un selector XPath se construye utilizando rutas que representan la jerarquía del documento. Existen dos tipos principales de rutas:
- Rutas absolutas: Comienzan desde el nodo raíz y describen el camino completo hasta el elemento objetivo. Por ejemplo:
/html/body/div/header/h1
Esta ruta selecciona el elemento <h1>
que se encuentra dentro de <header>
, que a su vez está dentro de <div>
en el <body>
del documento.
- Rutas relativas: Inician desde el nodo actual, sin necesidad de especificar el camino completo desde la raíz. Se utilizan dos barras diagonales
//
para indicar una selección en cualquier parte del documento. Por ejemplo:
//h1
Selecciona todos los elementos <h1>
en el documento, sin importar su ubicación.
Los predicados se utilizan para filtrar nodos y se encierran entre corchetes []
. Permiten especificar condiciones como atributos o posiciones. Por ejemplo:
//input[@type='text']
Este selector encuentra todos los elementos <input>
cuyo atributo type
es igual a 'text'
.
Es posible usar el carácter *
como comodín para representar cualquier elemento. Por ejemplo:
//div/*
Selecciona todos los elementos que son hijos directos de cualquier <div>
.
Los atributos se seleccionan utilizando el símbolo @
. Por ejemplo, para seleccionar un elemento con un identificador específico:
//*[@id='principal']
Este XPath selecciona cualquier elemento que tenga un atributo id
con el valor 'principal'
.
Es común utilizar funciones integradas en XPath para crear selectores más robustos. Algunas de las funciones básicas incluyen:
text()
: Selecciona el contenido de texto de un nodo.contains()
: Verifica si una cadena contiene otra.starts-with()
: Comprueba si una cadena comienza con una secuencia específica.
Por ejemplo, para seleccionar un enlace cuyo texto contiene la palabra "Contacto":
//a[contains(text(),'Contacto')]
En Selenium con Java y JUnit 5, podemos aplicar estos selectores XPath para localizar elementos. A continuación, se muestra un ejemplo práctico:
@Test
void testBusquedaElementoConXPath() {
WebDriver driver = new ChromeDriver();
driver.get("https://www.ejemplo.com");
WebElement campoBusqueda = driver.findElement(By.xpath("//input[@name='q']"));
campoBusqueda.sendKeys("Selenium");
WebElement botonBuscar = driver.findElement(By.xpath("//button[@type='submit']"));
botonBuscar.click();
driver.quit();
}
En este ejemplo, utilizamos selectores XPath para encontrar el campo de búsqueda y el botón de enviar en la página. Observa cómo se emplean los predicados para especificar atributos concretos.
También es posible navegar por la estructura del DOM utilizando ejes XPath, aunque esto se explora en mayor detalle en secciones avanzadas. Por ahora, con la sintaxis básica es suficiente para la mayoría de los casos.
Para seleccionar elementos basados en su posición, usamos índices dentro de los predicados. Por ejemplo:
(//ul/li)[3]
Este selector elige el tercer <li>
dentro de cualquier <ul>
.
Es importante recordar que los índices en XPath comienzan en 1, no en 0.
Una buena práctica es mantener los selectores XPath lo más sencillos y claros posible, evitando la dependencia de rutas absolutas que pueden romperse ante cambios en la estructura de la página.
Utilizar XPath de forma eficaz requiere comprensión y práctica, pero ofrece una gran flexibilidad para localizar elementos en escenarios complejos donde otros selectores no son suficientes.
Sintaxis avanzada de XPath
La sintaxis avanzada de XPath ofrece herramientas poderosas para localizar elementos en el DOM de manera más precisa y flexible. Al aprovechar ejes y funciones avanzadas, podemos crear selectores que se adapten a estructuras complejas y dinámicas en las páginas web.
Una característica clave en XPath son los ejes, que permiten navegar por las relaciones entre nodos. Algunos de los ejes más utilizados son:
ancestor
: Selecciona todos los ancestros de un nodo.descendant
: Selecciona todos los descendientes de un nodo.following-sibling
: Selecciona todos los hermanos siguientes del nodo actual.preceding-sibling
: Selecciona todos los hermanos anteriores.parent
: Selecciona el nodo padre.child
: Selecciona los hijos directos.
Por ejemplo, para seleccionar un elemento <li>
que sigue a otro <li>
con una clase específica, podemos usar:
//li[@class='activo']/following-sibling::li
En este caso, estamos seleccionando el hermano siguiente de un <li>
con clase 'activo'
.
Las funciones avanzadas nos permiten manipular cadenas y números dentro de los selectores. Algunas funciones útiles incluyen:
contains()
: Comprueba si una cadena contiene una subsecuencia específica.starts-with()
: Verifica si una cadena comienza con una secuencia dada.ends-with()
: Determina si una cadena termina con cierta secuencia (disponible en XPath 2.0 y posteriores).text()
: Obtiene el contenido de texto de un nodo.normalize-space()
: Elimina espacios en blanco redundantes en una cadena.
Por ejemplo, para seleccionar un elemento cuyo texto empieza por "Oferta", utilizamos:
//span[starts-with(text(),'Oferta')]
Además, podemos combinar condiciones utilizando operadores lógicos como **and**
y **or**
. Esto nos permite filtrar elementos de manera más precisa. Por ejemplo:
//input[@type='checkbox' and @checked='checked']
Este selector localiza los checkbox que están marcados, combinando dos condiciones.
Es posible también utilizar posiciones y secuencias para seleccionar nodos específicos. Las funciones position()
y last()
son especialmente útiles. Por ejemplo, para seleccionar el último elemento de una lista:
(//ul/li)[last()]
En este selector, estamos accediendo al último elemento <li>
dentro de cualquier <ul>
.
A veces, necesitamos navegar entre diferentes niveles del DOM. El uso de ejes como parent
y child
facilita esta tarea. Por ejemplo, para seleccionar el padre de un elemento con un atributo específico:
//div[@id='hijo']/parent::*
Aquí, estamos seleccionando el padre del <div>
que tiene id='hijo'
.
En Selenium, podemos aplicar estos selectores avanzados en nuestras pruebas.
@Test
void testSeleccionAvanzadaConXPath() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var menu = driver.findElement(By.xpath("//ul[@id='menu-principal']/li[3]/a"));
menu.click();
var subElemento = driver.findElement(By.xpath("//div[contains(@class,'contenido')]/descendant::p[normalize-space(text())='Texto específico']"));
subElemento.click();
}
}
En este código, utilizamos un try-with-resources de Java para gestionar el ChromeDriver
. Observa cómo aplicamos un selector XPath que combina contains()
, descendant::
y normalize-space()
para localizar un párrafo con un texto específico dentro de un contenedor.
Otra situación común es interactuar con elementos basados en su hermano precedente o siguiente. Por ejemplo:
//label[text()='Correo electrónico']/following-sibling::input
Este selector encuentra el <input>
que sigue al <label>
cuyo texto es "Correo electrónico". Es útil cuando los elementos no tienen atributos únicos, pero su posición relativa es constante.
También es posible utilizar expresiones más complejas, como seleccionar elementos basados en valores parciales de atributos:
//div[contains(@data-atributo,'parcial')]
Aquí, seleccionamos los <div>
cuyo atributo data-atributo
contiene la cadena "parcial".
Las funciones matemáticas y de secuencia permiten operaciones aún más avanzadas. Aunque no siempre son necesarias, conocer su existencia amplía nuestras posibilidades. Por ejemplo, podemos seleccionar los primeros cinco elementos de una lista:
//ul/li[position() <= 5]
Es importante destacar que en XPath, los índices comienzan en 1, no en 0. Esto es crucial al utilizar funciones como position()
.
Para mejorar la eficiencia de los selectores, es recomendable:
- Evitar rutas absolutas que dependan de la estructura completa del DOM.
- Utilizar atributos únicos siempre que sea posible.
- Combinar condiciones para reducir ambigüedades.
- Probar los selectores en las herramientas de desarrollo del navegador antes de implementarlos.
Por ejemplo, si necesitamos seleccionar un botón que no tiene un identificador único pero siempre tiene una clase específica y está dentro de un contenedor conocido, podemos hacer:
//div[@id='contenedor']//button[contains(@class,'btn-primario')]
En este caso, utilizamos el operador //
para buscar en todos los niveles dentro del contenedor con id='contenedor'
, y luego filtramos los botones que contienen la clase 'btn-primario'
.
@Test
void testManejoListaElementosConXPath() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var elementos = driver.findElements(By.xpath("//ul[@class='lista-items']/li"));
elementos.stream()
.filter(elemento -> elemento.getText().contains("Especial"))
.findFirst()
.ifPresent(WebElement::click);
}
}
En este ejemplo, obtenemos una lista de elementos <li>
, y luego aplicamos un stream para filtrar y encontrar el que contiene "Especial", realizando un clic sobre él si está presente.
Al construir selectores avanzados, es fundamental mantener un balance entre precisión y flexibilidad. Selectores demasiado específicos pueden romperse ante pequeños cambios en la página, mientras que selectores muy genéricos pueden seleccionar elementos incorrectos.
Finalmente, recordar que desde Selenium 4.6 en adelante, la detección de drivers es automática. Por lo tanto, no es necesario configurar manualmente el WebDriver, simplificando la inicialización en nuestras pruebas.
La maestría en sintaxis avanzada de XPath nos permite crear scripts de prueba más robustos y adaptables, optimizando la interacción con aplicaciones web complejas.
Sintaxis básica de selectores CSS
En Selenium, los selectores CSS son fundamentales para localizar elementos en el DOM de forma eficiente y precisa. Estos selectores emplean la misma sintaxis que las hojas de estilo CSS, lo que facilita su uso si ya se conocen las normas de estilo web.
Los selectores CSS pueden identificar elementos basándose en varios criterios:
- Selector de etiqueta: Selecciona todos los elementos de un tipo determinado. Por ejemplo,
input
selecciona todos los campos de entrada. - Selector de clase: Utiliza un punto
.
seguido del nombre de la clase. Por ejemplo,.boton
selecciona todos los elementos con la claseboton
. - Selector de ID: Utiliza una almohadilla
#
seguida del identificador. Por ejemplo,#formulario
selecciona el elemento con el IDformulario
. - Selector de atributo: Selecciona elementos que poseen un atributo específico. Por ejemplo,
[type='email']
selecciona todos los elementos cuyo atributotype
es igual aemail
.
Es posible combinar selectores para aumentar la especificidad. Por ejemplo, input.boton
selecciona todos los elementos <input>
que además tienen la clase boton
.
En las pruebas con Selenium y JUnit 5, podemos utilizar estos selectores de la siguiente manera:
@Test
void testLocalizacionConSelectoresCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var campoEmail = driver.findElement(By.cssSelector("input#correo"));
campoEmail.sendKeys("usuario@ejemplo.com");
var botonEnviar = driver.findElement(By.cssSelector("button.boton-enviar"));
botonEnviar.click();
}
}
En este ejemplo, utilizamos un selector de ID (input#correo
) y un selector de clase (button.boton-enviar
) para localizar los elementos deseados.
Los selectores CSS también permiten seleccionar elementos en función de su posición o relación en el DOM:
- Selector de hijo directo: Utiliza el símbolo
>
para seleccionar elementos que son hijos directos. Por ejemplo,ul > li
selecciona todos los<li>
que son hijos directos de un<ul>
. - Selector de descendiente: Selecciona elementos que son descendientes, en cualquier nivel. Por ejemplo,
div .activo
selecciona todos los elementos con la claseactivo
que están dentro de un<div>
, sin importar la profundidad. - Selector de hermano adyacente: Utiliza el símbolo
+
para seleccionar elementos que son hermanos inmediatos. Por ejemplo,h2 + p
selecciona el<p>
que sigue inmediatamente a un<h2>
.
Podemos aplicar estos selectores en nuestras pruebas:
@Test
void testSelectoresRelacionConCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var primerItemMenu = driver.findElement(By.cssSelector("nav > ul > li:first-child"));
primerItemMenu.click();
var mensaje = driver.findElement(By.cssSelector("h2 + p"));
assertTrue(mensaje.isDisplayed());
}
}
En este código, utilizamos el selector de hijo directo y el selector de hermano adyacente para interactuar con los elementos correspondientes.
Los selectores de atributos avanzados permiten localizar elementos basados en patrones dentro de los atributos:
- Comienza con (
^=
):[href^='https']
selecciona elementos cuyo atributohref
empieza porhttps
. - Termina con (
$=
):[src$='.png']
selecciona elementos cuyo atributosrc
termina con.png
. - Contiene (
*=
):[class*='icono']
selecciona elementos cuya clase contiene la palabraicono
.
Estos selectores son útiles para elementos dinámicos o cuando no se conoce el valor exacto del atributo.
@Test
void testSelectoresAtributosAvanzadosConCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var enlacesDescarga = driver.findElements(By.cssSelector("a[href$='.pdf']"));
enlacesDescarga.forEach(enlace -> enlace.click());
}
}
Aquí, utilizamos un selector de atributo avanzado para encontrar todos los enlaces que apuntan a archivos PDF y los procesamos con un bucle.
Las pseudoclases permiten seleccionar elementos basados en su estado o posición:
:hover
: Selecciona elementos cuando el ratón está sobre ellos.:nth-child(n)
: Selecciona el elemento que es el n-ésimo hijo de su padre.:first-of-type
: Selecciona el primer elemento de su tipo entre los hermanos.
Por ejemplo:
@Test
void testPseudoclasesConCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var tercerElemento = driver.findElement(By.cssSelector("ul.lista li:nth-child(3)"));
tercerElemento.click();
}
}
En este caso, utilizamos :nth-child(3)
para seleccionar el tercer elemento de una lista.
Es importante tener en cuenta que los selectores CSS son sensibles a la especificidad y al heredado de estilos en el DOM. Por ello, es recomendable ser lo más preciso posible sin excederse, para evitar selecciones incorrectas.
Los selectores universales y comodines también pueden ser útiles:
- El asterisco
*
selecciona todos los elementos. Por ejemplo,div *
selecciona todos los elementos dentro de un<div>
. - Los selectores de grupo permiten seleccionar múltiples elementos. Por ejemplo,
h1, h2, h3
selecciona todos los encabezados de nivel 1 a 3.
En la práctica:
@Test
void testSelectoresUniversalesYGruposConCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var titulos = driver.findElements(By.cssSelector("h1, h2, h3"));
titulos.forEach(titulo -> System.out.println(titulo.getText()));
}
}
Esta prueba recopila los textos de todos los encabezados principales de la página.
Para finalizar, recordar que los nombres de clases múltiples se pueden seleccionar combinando las clases en un solo selector:
.button.primary
Selecciona elementos que tienen tanto la clase button
como primary
.
En nuestras pruebas:
@Test
void testSeleccionMultipleClasesConCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var botonesPrimarios = driver.findElements(By.cssSelector(".button.primary"));
botonesPrimarios.forEach(boton -> boton.click());
}
}
Aquí, interactuamos con todos los botones que cumplen ambas clases, asegurando una interacción precisa con los elementos deseados.
Utilizar selectores CSS de forma eficaz mejora la eficiencia y mantenibilidad de nuestras pruebas automatizadas. Al familiarizarnos con su sintaxis básica, podemos localizar elementos con mayor facilidad y adaptarnos a cambios en el diseño de la página.
Sintaxis avanzada de selectores CSS
Los selectores CSS avanzados permiten localizar elementos en el DOM con mayor precisión y flexibilidad. Al dominar estas técnicas, podemos crear selectores que se adaptan a estructuras complejas y dinámicas en nuestras pruebas con Selenium.
Una de las características más importantes son las pseudoclases avanzadas, que seleccionan elementos en función de su estado o posición relativa. Algunas de las más útiles son:
:nth-last-child(n)
: Selecciona el elemento que es el enésimo hijo desde el final. Por ejemplo,ul li:nth-last-child(1)
selecciona el último elemento<li>
dentro de un<ul>
.:nth-of-type(n)
: Selecciona el enésimo elemento de un tipo específico entre sus hermanos. Por ejemplo,div p:nth-of-type(2)
selecciona el segundo<p>
dentro de un<div>
.:not(selector)
: Selecciona todos los elementos que no coinciden con el selector especificado. Por ejemplo,input:not([type='submit'])
selecciona todos los<input>
que no son de tiposubmit
.
Los pseudoelementos nos permiten seleccionar partes específicas de elementos. Aunque no son directamente soportados por Selenium para acciones, pueden ser útiles para verificar estilos. Ejemplos de pseudoelementos son ::before
y ::after
.
Los selectores de estado son esenciales para interactuar con formularios:
:enabled
y:disabled
: Seleccionan elementos que están habilitados o deshabilitados. Por ejemplo,button:enabled
selecciona todos los botones que están habilitados.:checked
: Selecciona elementos que están marcados o seleccionados, como casillas de verificación o botones de radio. Por ejemplo,input[type='checkbox']:checked
.
También podemos utilizar el combinador de hermanos generales ~
, que selecciona elementos hermanos que siguen después del elemento actual, sin importar si son adyacentes o no. Por ejemplo, h2 ~ p
selecciona todos los párrafos <p>
que siguen a un <h2>
en el mismo nivel.
Los selectores de atributos avanzados permiten coincidencias más precisas:
[atributo|='valor']
: Selecciona elementos cuyo atributo comienza con un valor específico seguido de un guion. Útil para seleccionar idiomas. Por ejemplo,[lang|='es']
selecciona elementos en español.[atributo~='valor']
: Selecciona elementos cuyo atributo contiene una palabra exacta. Por ejemplo,[class~='destacado']
selecciona elementos cuya clase incluye exactamente la palabradestacado
.
Ejemplo práctico en una prueba con JUnit 5 y Selenium 4.26:
@Test
void testSelectoresAvanzadosCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var ultimoElementoLista = driver.findElement(By.cssSelector("ul.lista > li:nth-last-child(1)"));
ultimoElementoLista.click();
var camposHabilitados = driver.findElements(By.cssSelector("input:enabled"));
camposHabilitados.forEach(campo -> campo.sendKeys("Texto de prueba"));
var elementosNoSeleccionados = driver.findElements(By.cssSelector("input[type='checkbox']:not(:checked)"));
elementosNoSeleccionados.forEach(WebElement::click);
var parrafosDespuesDeTitulo = driver.findElements(By.cssSelector("h2 ~ p"));
parrafosDespuesDeTitulo.forEach(parrafo -> System.out.println(parrafo.getText()));
var elementosIdiomaEspañol = driver.findElements(By.cssSelector("[lang|='es']"));
elementosIdiomaEspañol.forEach(elemento -> System.out.println(elemento.getAttribute("lang")));
}
}
En este código, utilizamos varios selectores avanzados para interactuar con elementos específicos:
ul.lista > li:nth-last-child(1)
: Selecciona el último<li>
dentro de una lista con claselista
.input:enabled
: Selecciona todos los campos de entrada que están habilitados.input[type='checkbox']:not(:checked)
: Selecciona las casillas de verificación que no están marcadas.h2 ~ p
: Selecciona todos los párrafos que siguen a un<h2>
en el mismo nivel jerárquico.[lang|='es']
: Selecciona elementos cuyo atributolang
indica idioma español.
Otra técnica útil es combinar pseudoclases y selectores de atributo para lograr una selección más precisa:
@Test
void testCombinacionSelectoresCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var enlacesExternos = driver.findElements(By.cssSelector("a[href^='http']:not([href*='ejemplo.com'])"));
enlacesExternos.forEach(WebElement::click);
var camposObligatorios = driver.findElements(By.cssSelector("input[required]:not(:disabled)"));
camposObligatorios.forEach(campo -> campo.sendKeys("Información requerida"));
}
}
En este ejemplo, hemos utilizado:
a[href^='http']:not([href*='ejemplo.com'])
: Selecciona todos los enlaces que comienzan conhttp
y no contienenejemplo.com
en la URL, es decir, enlaces externos.input[required]:not(:disabled)
: Selecciona todos los campos obligatorios que no están deshabilitados.
Los selectores de posición pueden utilizar expresiones funcionales más complejas. Por ejemplo, :nth-child(2n+1)
selecciona todos los elementos impares. Podemos aplicarlo de la siguiente manera:
@Test
void testSelectoresPosicionCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var filasImpares = driver.findElements(By.cssSelector("table tr:nth-child(2n+1)"));
filasImpares.forEach(fila -> System.out.println(fila.getText()));
}
}
Aquí, seleccionamos las filas impares de una tabla, lo cual puede ser útil para validar datos o aplicar acciones específicas.
Los pseudoclases estructurales como :first-child
, :last-child
, :only-child
también proporcionan formas precisas de seleccionar elementos en función de su posición. Por ejemplo:
@Test
void testPseudoclasesEstructuralesCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var unicoElemento = driver.findElement(By.cssSelector("div.contenedor > p:only-child"));
System.out.println(unicoElemento.getText());
var primerElemento = driver.findElement(By.cssSelector("ul.menu > li:first-child"));
primerElemento.click();
}
}
En este caso, utilizamos:
p:only-child
: Selecciona el párrafo que es el único hijo de su padre.li:first-child
: Selecciona el primer elemento de una lista.
Los pseudoclases de estado del usuario como :focus
, :active
detectan el estado de interacción del usuario con los elementos. Aunque su uso es limitado en Selenium (ya que el estado puede cambiar rápidamente), pueden ser útiles para comprobar comportamientos específicos.
Además, los pseudoclases funcionales como :is()
, :where()
, introducidas en CSS3, permiten simplificar selectores complejos:
@Test
void testPseudoclasesFuncionalesCSS() {
try (var driver = new ChromeDriver()) {
driver.get("https://www.ejemplo.com");
var elementosEspeciales = driver.findElements(By.cssSelector(":is(h1, h2, h3).titulo"));
elementosEspeciales.forEach(elemento -> System.out.println(elemento.getText()));
}
}
En este ejemplo, :is(h1, h2, h3).titulo
selecciona cualquier <h1>
, <h2>
o <h3>
que tenga la clase titulo
.
Los selectores relacionales también son muy útiles. El combinador de hijo adyacente +
y el combinador de hermano general ~
permiten seleccionar elementos en relación con otros. Por ejemplo:
label + input
: Selecciona el<input>
que sigue inmediatamente a un<label>
.h3 ~ p
: Selecciona todos los párrafos que siguen a un<h3>
, no necesariamente adyacentes.
Finalmente, es importante considerar el uso de selectores anidados y combinaciones complejas para casos específicos. Sin embargo, debemos equilibrar la precisión con la legibilidad y rendimiento de los selectores. Selectores demasiado complejos pueden dificultar el mantenimiento de las pruebas.
Al utilizar selectores CSS avanzados, mejoramos la flexibilidad y robustez de nuestras pruebas automatizadas, permitiendo interactuar con elementos de manera más precisa y eficiente.
Construir selectores eficientes y robustos
Al automatizar pruebas con Selenium, es esencial crear selectores que sean tanto eficientes como robustos. Un selector bien diseñado no solo mejora el rendimiento de las pruebas, sino que también reduce la probabilidad de fallos ante cambios en la página web.
Para construir selectores eficientes, es recomendable preferir selectores CSS sobre XPath cuando sea posible. Los selectores CSS suelen ser más rápidos y consumen menos recursos, lo que acelera la ejecución de las pruebas. Por ejemplo, en lugar de usar un XPath como //button[@id='enviar']
, es más eficiente utilizar button#enviar
.
La unicidad del selector es clave para garantizar que se está interactuando con el elemento correcto. Utilizar atributos únicos como id
o name
ayuda a crear selectores más precisos. Si un elemento tiene un identificador único, es preferible utilizarlo directamente: By.id("nombreUsuario")
.
Cuando los atributos únicos no están disponibles, es útil combinar varias clases o atributos para construir un selector más específico. Por ejemplo, By.cssSelector("input[type='email'].campo-obligatorio")
localiza específicamente un campo de correo electrónico que es obligatorio.
Es importante evitar los selectores demasiado largos o complejos que dependan de la estructura completa del DOM. Los selectores relativos son más robustos ante cambios en la estructura de la página. Por ejemplo, en lugar de utilizar un selector absoluto como html > body > div > form > input
, es mejor usar form input
.
Para elementos dinámicos cuyos atributos pueden cambiar, es recomendable utilizar funciones como starts-with()
o contains()
en XPath, o los operadores ^=
y *=
en selectores CSS. Por ejemplo:
WebElement botonContinuar = driver.findElement(By.cssSelector("button[id^='continuar-']"));
Este selector localiza cualquier botón cuyo id
comience por 'continuar-'
, siendo más flexible ante cambios en el identificador.
La mantenibilidad de las pruebas se mejora al usar selectores que sean menos propensos a romperse. Evita basar los selectores en atributos auto-generados o en posiciones que puedan cambiar. Por ejemplo, en lugar de seleccionar el tercer elemento de una lista con ul li:nth-child(3)
, es preferible identificar el elemento por un atributo significativo.
Es aconsejable trabajar en colaboración con los desarrolladores para incluir identificadores únicos en los elementos clave. Añadir atributos como data-testid
puede facilitar la creación de selectores estables:
WebElement botonLogin = driver.findElement(By.cssSelector("[data-testid='boton-login']"));
La legibilidad de los selectores también es importante. Selectores claros y concisos facilitan el mantenimiento y la comprensión del código. Comentarios breves pueden ayudar a explicar selectores complejos cuando sea necesario.
Antes de implementar un selector en las pruebas, es útil verificar su validez en las herramientas de desarrollo del navegador. Esto asegura que el selector apunta al elemento correcto y mejora la eficiencia del proceso de automatización.
Para mejorar el rendimiento, minimiza el uso de ejes XPath como ancestor
, preceding-sibling
, ya que pueden ser menos eficientes. Siempre que sea posible, utiliza selectores directos y evita navegaciones profundas en el DOM.
En entornos donde la interfaz de usuario es altamente dinámica, es beneficioso implementar una capa de abstracción para los selectores. Utilizar métodos que encapsulen la lógica de localización permite gestionar los cambios en un solo lugar:
public WebElement obtenerCampoContraseña() {
return driver.findElement(By.id("contraseña"));
}
List<WebElement> opciones = driver.findElements(By.cssSelector(".opcion-menu"));
opciones.stream()
.filter(opcion -> opcion.getText().equals("Configuración"))
.findFirst()
.ifPresent(WebElement::click);
La sincronización entre las pruebas y la carga de elementos es crucial. Asegúrate de que los selectores sean utilizados en combinación con esperas adecuadas para evitar tiempos muertos o interacciones prematuras.
Finalmente, mantén tus selectores actualizados con respecto a cambios en la aplicación. Una revisión periódica y la refactorización de los selectores contribuyen a la robustez y eficiencia de las pruebas automatizadas.
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.
Introducción A Selenium
Introducción Y Entorno
Crear Proyecto Selenium
Introducción Y Entorno
Fundamentos Webdriver
Fundamentos Webdriver
Ejecución Remota Con Remotewebdriver
Fundamentos Webdriver
Localización De Elementos Web
Localización De Elementos
Dominio De Selectores Xpath Y Css
Localización De Elementos
Interacción Con Interfaz De Usuario
Localización De Elementos
Interacción Con Formularios
Localización De Elementos
Esperas Implícitas Y Explícitas
Localización De Elementos
Patrón Page Object Model (Pom)
Localización De Elementos
Objetivos de aprendizaje de esta lección
- Aprender qué es XPath
- Aprender la sintaxis básica de xpath
- Aprender la sintaxis avanzada de xpath
- Aprender la sintaxis básica de selectores CSS
- Aprender la sintaxis avanzada de selectores CSS