Selenium es uno de los frameworks más utilizados para programar y automatizar las interacciones de un usuario sobre una aplicación web. Se puede utilizar a través de lenguajes como Java, Python, C#, Javascript y Kotlin. Es habitual combinarlo con Cucumber para aplicar la metodología BDD simplificando además la estructuración y ejecución de las pruebas. En este post vamos a explicar todas las pautas para automatizar pruebas con Selenium paso a paso.
Qué vas as ver en esta entrada
Entorno
De las distintas configuraciones posibles utilizamos:
- Selenium 4.6.0
- Java 17
- Maven 3.8.6
- JUnit 5
- Cucumber 7.9.0
- Eclipse + Plugin Cucumber + Plugin Natural (Soporte a sintaxis Gherkin)
- Sitio web sobre el que automatizar las pruebas: GreenKart.
Conceptos básicos
Partiendo de un proyecto de Maven se añaden las siguientes dependencias:
También es necesario descargar el WebDriver para el navegador sobre el que queramos ejecutar las pruebas. En este caso se utiliza el Web Driver para Chrome.
Inicializar sesión
Existen distintas posibilidades a la hora de iniciar una sesión con un browser y crear el WebDriver. Por ejemplo para interactuar con Chrome teniendo el driver correspondiente descargado en una ruta path/to/driver:
System.setProperty("webdriver.chrome.driver", "path/to/driver");
WebDriver driver = new ChromeDriver();
Sólo se depende del tipo concreto de driver en la instanciación. Las instrucciones al navegador son independientes del tipo de driver y navegador utilizado.
Navegar a una página web
Con el método get indicar la url a la que se desea navegar:
driver.get("https://rahulshettyacademy.com/seleniumPractise/#/");
Obtener información del browser
Se puede obtener todo tipo de información de la página que acabamos de cargar, por ejemplo, título, url, manejadores de ventana…
String title = driver.getTitle();
String currentUrl = driver.getCurrentUrl();
String windowHandle = driver.getWindowHandle();
Establecer estrategia de espera
Por la naturaleza asíncrona de carga de elementos del DOM es necesario establecer una estrategia de espera en el WebDriver a la hora de buscar algunos elementos. La forma más simple, pero no la más recomendada, es utilizar un wait implícito.
driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500));
Otra posibilidad es emplear wait explícito. En el ejemplo establecemos una espera máxima de dos segundos hasta que el elemento buscado se cargue en el DOM y además su estado sea visible.
WebElement orderBtn = new WebDriverWait(driver, Duration.ofSeconds(2)) .until(ExpectedConditions.visibilityOfElementLocated( By.xpath("//button[contains(text(),'Place Order')]")));
Existen otras estrategias de espera. Se pueden consultar en la sección waits de la documentación oficial.
Buscar elementos
Selenium dispone de métodos para buscar elementos en el DOM. Soporta selectores basados en clases CSS, identificadores, tipos de tag en DOM y expresiones XPath. Están centralizados en la clase By.
Veamos la forma de recuperar desde java distintos elementos:
WebElement input = driver.findElement(By.xpath("//input[@type='search']"));
WebElement linkTopDeals = driver.findElement(By.linkText("Top Deals"));
WebElement increment = driver.findElement(By.cssSelector("a.increment"));
WebElement quantityInput = driver.findElement(By.xpath("//input[@type='number']"));
Realizar acciones sobre elementos
Cualquier interacción que el usuario pueda hacer con la web se puede realizar mediante el api. Por ejemplo enviar caracteres a un input, hacer click, submit o limpiar cajas de texto.
Simulamos una búsqueda de con nombre “Broco” y a continuación pulsamos en el icono ‘+’ para incrementar el número de unidades.
searchInput.sendKeys("Broco");
increment.click();
Obtener información de elementos
Queremos comprobar que el número de unidades de “Brócoli” se ha incrementado al pulsar en el icono ‘+’. Para ello obtenemos el valor del atributo value del input correspondiente:
String quantitySelected = quantityInput.getAttribute("value");
Finalizar sesión
Para cerrar el navegador. A partir de esta instrucción no se pueden enviar más comandos al driver.
driver.close();
Integración con JUnit 5
Se utiliza la dependencia:
Un uso típico consiste en utilizar WebDriver dentro de un test unitario para cargar una página y realizar ciertas interacciones con sus elementos.
Para ilustrar el funcionamiento vamos a unir todos los conceptos que llevamos vistos en el siguiente test.
- Se navega a la página de compras.
- Una vez cargada programamos una búsqueda de productos que contengan el texto “Broco”.
- Para el resultado obtenido pulsamos en el icono ‘+’.
- Utilizamos aserciones para comprobar que seleccionamos dos productos. También comprobamos el título de la página.
public class SeleniumConceptsTest {
@Test
void testConcepts() {
// Start session
System.setProperty( "webdriver.chrome.driver",
Paths.get(System.getProperty("user.dir"), "src/test/resources/chromedriver.exe").toString());
WebDriver driver = new ChromeDriver();
// Navigate to web page
driver.get("https://rahulshettyacademy.com/seleniumPractise/#/");
// Request browser information
String title = driver.getTitle();
String currentUrl = driver.getCurrentUrl();
String windowHandle = driver.getWindowHandle();
// Waiting strategy
driver.manage().timeouts().implicitlyWait(Duration.ofMillis(500));
// Finding elements
WebElement searchInput = driver.findElement( By.xpath("//input[@type='search']"));
WebElement linkTopDeals = driver.findElement(By.linkText("Top Deals"));
WebElement increment = driver.findElement(By.cssSelector("a.increment"));
WebElement quantityInput = driver.findElement( By.xpath("//input[@type='number']"));
// Interacting with elements
searchInput.sendKeys("Broco");
increment.click();
// Request element information
String quantitySelected = quantityInput.getAttribute("value");
Assertions.assertEquals(2, Integer.valueOf(quantitySelected));
Assertions.assertEquals("GreenKart - veg and fruits kart", title);
// End session
driver.close();
}
}
Ejecutando el test en el IDE se lanza un browser Chrome donde se van llevando a cabo acciones programadas como se muestra en el vídeo.
Además disponemos de los resultados del test:
Integración con Cucumber
Selenium se integra con facilidad con Cucumber. Esto facilita un enfoque BDD de los test, la organización y mantenimiento del código resultante.
Se precisan la siguientes dependencias:
Construyamos un ejemplo que verifique que los usuarios pueden añadir productos al carrito y acceder a la página de realizar pedido.
El fichero Cart.feature contiene la especificación del escenario de prueba en Gherkin:
# -- FILE: features/Cart.feature
Feature: User experience with Cart
Scenario Outline: Add product to cart
Given User is on GreenCart landing page
When user searched with Shortname <Name> and extracted actual name of product
And added 4 items of the selected product to cart
Then verify user has ability to checkout order
Examples:
| Name |
| Cucum |
| Carrot |
Cada línea del fichero se mapea con métodos de java, concepto que se conoce por Step Definitions. La vinculación se realiza mediante anotaciones específicas.
public class LandingPageStepDefinitions {
private TestContextSetup testContextSetup;
private LandingPage landingPage;
public LandingPageStepDefinitions(TestContextSetup testContextSetup) {
this.testContextSetup = testContextSetup;
landingPage = testContextSetup.getPageObjectManager().getLandingPage();
}
@Given("User is on GreenCart landing page")
public void user_is_on_green_cart_landing_page() {
Assertions.assertTrue(landingPage.getTitle().contains("GreenKart"));
}
@When("^user searched with Shortname (.+) and extracted actual name of product$")
public void user_searched_with_shortname_and_extracted_actual_name_of_product(String shortName) {
landingPage.searchItem(shortName);
testContextSetup.setLandingPageProductName(landingPage.getProductName());
}
@When("added {int} items of the selected product to cart")
public void added_items_of_the_selected_product_to_cart(Integer quantity) {
landingPage.incrementQuantity(quantity);
landingPage.addToCart();
}
}
public class CheckoutPageStepDefinitions {
private TestContextSetup testContextSetup;
private CheckoutPage checkoutPage;
public CheckoutPageStepDefinitions(TestContextSetup testContextSetup) {
super();
this.testContextSetup = testContextSetup;
this.checkoutPage = testContextSetup. getPageObjectManager().getCheckoutPage();
}
@Then("verify user has ability to checkout order")
public void verify_user_has_ability_to_checkout_order() {
checkoutPage.checkoutItems();
}
}
Una buena práctica de cara al mantenimiento de los test es no incluir el código de Selenium en los StepDefinition. Crear otra clase distinta para mantener los selectores y métodos propios del api de Selenium.
Lo ponemos en práctica con la clase CheckoutPage. Mantiene los selectores para el icono del carrito y el botón de proceder al checkout. Además implementa un método checkoutItems que interactúa con dichos componentes haciendo click en primer lugar en el icono del carrito y posteriormente en el de checkout.
public class CheckoutPage {
private static final By CART_BAG = By.cssSelector("[alt='Cart']");
private static final By CHECKOUT_BUTTON = By.xpath("//button[contains(text(),'PROCEED TO CHECKOUT')]");
private WebDriver driver;
public CheckoutPage(WebDriver driver) {
super();
this.driver = driver;
}
public void checkoutItems() {
driver.findElement(CART_BAG).click();
driver.findElement(CHECKOUT_BUTTON).click();
}
}
Ejecutando el escenario se realizan las dos interacciones programadas tal y cómo podemos ver en el vídeo.
En el IDE se comprueba que todos los test han sido correctos.
Conclusiones
Hemos visto cómo el API de Selenium permite interactuar con los distintos elementos de una página web. Además utilizando JUnit 5 comprobamos cómo se pueden realizar distintas verificaciones automáticas. Finalmente, vemos una breve introducción de cómo usar Cucumber para estructurar y reutilizar el código de las pruebas hechas con Selenium. Esperamos que os haya sido útil este artículo para automatizar pruebas con Selenium.
Los ejemplos de este post están accesibles en el repositorio de github.
Referencias de interés: