TL;DR

  • Selenium WebDriver automatiza navegadores reales (Chrome, Firefox, Safari, Edge) para probar aplicaciones web
  • Empieza con Python — sintaxis más simple, feedback más rápido para principiantes
  • Domina localizadores (ID, CSS, XPath) y esperas explícitas antes de escribir tests complejos
  • Usa Page Object Model desde el primer día — refactorizar después es doloroso

Ideal para: QA engineers empezando automatización de navegadores, desarrolladores escribiendo tests E2E Omite si: Solo necesitas testing de APIs (usa Postman) o ya conoces Playwright/Cypress Tiempo de lectura: 15 minutos

Tu primer test de Selenium probablemente fallará. No porque Selenium esté roto, sino porque la automatización web tiene problemas de timing que no has encontrado antes. Los elementos cargan asincrónicamente. Los botones se vuelven clickeables después de que JavaScript se ejecuta. Los formularios validan en blur.

Este tutorial te enseña Selenium de la manera correcta — manejando estos desafíos del mundo real desde el inicio.

¿Qué es Selenium WebDriver?

Selenium WebDriver es una herramienta de automatización de navegadores que controla navegadores reales programáticamente. A diferencia de herramientas que simulan navegadores, Selenium maneja instancias reales de Chrome, Firefox, Safari y Edge.

Componentes principales:

  • WebDriver API — bindings de lenguaje (Python, Java, C#, JavaScript, Ruby)
  • Browser drivers — ChromeDriver, GeckoDriver, SafariDriver
  • Selenium Grid — ejecución distribuida de tests en múltiples máquinas

Selenium es gratuito, open-source y mantenido por una gran comunidad. Es la base sobre la que herramientas como Appium (móvil) y Selenide (wrapper Java) se construyen.

Configuración del Entorno

Configuración Python

# Instalar Selenium
pip install selenium

# Instalar WebDriver Manager (descarga drivers automáticamente)
pip install webdriver-manager
# test_first.py
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# Gestión automática de drivers
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

driver.get("https://example.com")
print(f"Título de página: {driver.title}")

driver.quit()

Configuración Java

<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.18.1</version>
    </dependency>
    <dependency>
        <groupId>io.github.bonigarcia</groupId>
        <artifactId>webdrivermanager</artifactId>
        <version>5.7.0</version>
    </dependency>
</dependencies>
// FirstTest.java
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import io.github.bonigarcia.wdm.WebDriverManager;

public class FirstTest {
    public static void main(String[] args) {
        WebDriverManager.chromedriver().setup();
        WebDriver driver = new ChromeDriver();

        driver.get("https://example.com");
        System.out.println("Título: " + driver.getTitle());

        driver.quit();
    }
}

Estrategias de Localizadores

Los localizadores encuentran elementos en la página. Elige la estrategia correcta para tests mantenibles.

Orden de Prioridad (Mejor a Peor)

EstrategiaEjemploCuándo Usar
ID#login-buttonIDs únicos (mejor opción)
Name[name="email"]Inputs de formularios
CSS Selector.btn.primaryMayoría de elementos
Link TextSign InLinks con texto estable
XPath//div[@class='card']Relaciones complejas

Ejemplos Python

from selenium.webdriver.common.by import By

# Por ID (más rápido, más confiable)
driver.find_element(By.ID, "username")

# Por CSS Selector (flexible, legible)
driver.find_element(By.CSS_SELECTOR, "button.submit-btn")
driver.find_element(By.CSS_SELECTOR, "[data-testid='login']")

# Por XPath (cuando CSS falla)
driver.find_element(By.XPATH, "//button[contains(text(), 'Submit')]")
driver.find_element(By.XPATH, "//div[@class='form']//input[@type='email']")

# Por Link Text
driver.find_element(By.LINK_TEXT, "Forgot Password?")
driver.find_element(By.PARTIAL_LINK_TEXT, "Forgot")

Ejemplos Java

import org.openqa.selenium.By;

// Por ID
driver.findElement(By.id("username"));

// Por CSS Selector
driver.findElement(By.cssSelector("button.submit-btn"));
driver.findElement(By.cssSelector("[data-testid='login']"));

// Por XPath
driver.findElement(By.xpath("//button[contains(text(), 'Submit')]"));

// Por Link Text
driver.findElement(By.linkText("Forgot Password?"));

Esperas: El Concepto Más Importante

90% de los tests Selenium inestables fallan por timing. Los elementos no están listos cuando tu código intenta interactuar con ellos.

Tipos de Esperas

TipoCómo FuncionaCuándo Usar
ImplicitConsulta DOM por N segundosNunca (global, oculta problemas)
ExplicitEspera condición específicaSiempre (preciso, legible)
FluentExplicit + polling personalizadoElementos de carga lenta

Esperas Explícitas (Python)

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

wait = WebDriverWait(driver, 10)  # Máx 10 segundos

# Esperar que elemento sea clickeable
button = wait.until(EC.element_to_be_clickable((By.ID, "submit")))
button.click()

# Esperar que elemento sea visible
message = wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "success")))

# Esperar que elemento desaparezca
wait.until(EC.invisibility_of_element_located((By.ID, "loading")))

# Esperar texto
wait.until(EC.text_to_be_present_in_element((By.ID, "status"), "Complete"))

# Condición personalizada
wait.until(lambda d: len(d.find_elements(By.CLASS_NAME, "item")) > 5)

Esperas Explícitas (Java)

import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

// Esperar clickeable
WebElement button = wait.until(
    ExpectedConditions.elementToBeClickable(By.id("submit"))
);
button.click();

// Esperar visibilidad
WebElement message = wait.until(
    ExpectedConditions.visibilityOfElementLocated(By.className("success"))
);

// Esperar desaparición
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.id("loading")));

Ejemplo de Test Completo

Test de Login (Python)

# tests/test_login.py
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

class TestLogin:
    def setup_method(self):
        self.driver = webdriver.Chrome(
            service=Service(ChromeDriverManager().install())
        )
        self.driver.implicitly_wait(0)  # Deshabilitar implicit waits
        self.wait = WebDriverWait(self.driver, 10)

    def teardown_method(self):
        self.driver.quit()

    def test_successful_login(self):
        self.driver.get("https://example.com/login")

        # Llenar formulario
        email_input = self.wait.until(
            EC.visibility_of_element_located((By.ID, "email"))
        )
        email_input.send_keys("user@example.com")

        password_input = self.driver.find_element(By.ID, "password")
        password_input.send_keys("password123")

        # Enviar formulario
        submit_btn = self.driver.find_element(By.CSS_SELECTOR, "[type='submit']")
        submit_btn.click()

        # Verificar redirect a dashboard
        self.wait.until(EC.url_contains("/dashboard"))

        welcome_message = self.wait.until(
            EC.visibility_of_element_located((By.CLASS_NAME, "welcome"))
        )
        assert "Welcome" in welcome_message.text

    def test_invalid_credentials(self):
        self.driver.get("https://example.com/login")

        self.wait.until(
            EC.visibility_of_element_located((By.ID, "email"))
        ).send_keys("invalid@example.com")

        self.driver.find_element(By.ID, "password").send_keys("wrongpass")
        self.driver.find_element(By.CSS_SELECTOR, "[type='submit']").click()

        error_message = self.wait.until(
            EC.visibility_of_element_located((By.CLASS_NAME, "error"))
        )
        assert "Invalid credentials" in error_message.text

Page Object Model

Page Object Model (POM) separa estructura de página de lógica de tests. Cada página se convierte en una clase con elementos y acciones.

Python Page Object

# pages/login_page.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class LoginPage:
    URL = "https://example.com/login"

    # Localizadores
    EMAIL_INPUT = (By.ID, "email")
    PASSWORD_INPUT = (By.ID, "password")
    SUBMIT_BUTTON = (By.CSS_SELECTOR, "[type='submit']")
    ERROR_MESSAGE = (By.CLASS_NAME, "error")

    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    def open(self):
        self.driver.get(self.URL)
        self.wait.until(EC.visibility_of_element_located(self.EMAIL_INPUT))
        return self

    def login(self, email: str, password: str):
        self.driver.find_element(*self.EMAIL_INPUT).send_keys(email)
        self.driver.find_element(*self.PASSWORD_INPUT).send_keys(password)
        self.driver.find_element(*self.SUBMIT_BUTTON).click()

    def get_error_message(self) -> str:
        error = self.wait.until(
            EC.visibility_of_element_located(self.ERROR_MESSAGE)
        )
        return error.text
# tests/test_login_pom.py
import pytest
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage

class TestLoginPOM:
    def test_successful_login(self, driver):
        login_page = LoginPage(driver)
        login_page.open()
        login_page.login("user@example.com", "password123")

        dashboard = DashboardPage(driver)
        assert dashboard.is_loaded()
        assert "Welcome" in dashboard.get_welcome_text()

    def test_invalid_login(self, driver):
        login_page = LoginPage(driver)
        login_page.open()
        login_page.login("invalid@example.com", "wrongpass")

        assert "Invalid credentials" in login_page.get_error_message()

Modo Headless y CI/CD

from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless=new")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--window-size=1920,1080")

driver = webdriver.Chrome(options=options)

Integración con GitHub Actions

# .github/workflows/selenium-tests.yml
name: Selenium Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          pip install selenium pytest webdriver-manager

      - name: Run tests
        run: pytest tests/ -v --tb=short

      - name: Upload screenshots
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: screenshots
          path: screenshots/

Desarrollo de Tests Asistido por IA

Las herramientas de IA pueden acelerar el desarrollo de tests Selenium cuando se usan correctamente.

Lo que la IA hace bien:

  • Generar estrategias de localizadores desde snippets HTML
  • Convertir pasos de tests manuales a código Selenium
  • Escribir boilerplate de Page Object
  • Explicar mensajes de error crípticos
  • Sugerir estrategias de espera para escenarios específicos

Lo que aún necesita humanos:

  • Decidir qué tests automatizar
  • Depurar flakiness relacionada con timing
  • Elegir entre estrategias de localizadores para elementos ambiguos
  • Entender comportamiento específico de la aplicación

Prompt útil:

Tengo este formulario HTML:
<form id="login">
  <input type="email" name="email" placeholder="Email">
  <input type="password" name="password">
  <button type="submit">Sign In</button>
</form>

Escribe un test Selenium Python que:
1. Complete email y password
2. Envíe el formulario
3. Espere redirect a /dashboard
4. Verifique que aparece mensaje de bienvenida

Usa esperas explícitas y localizadores apropiados.

FAQ

¿Es difícil aprender Selenium?

Los básicos de Selenium toman 2-4 semanas. La API de WebDriver es directa — find_element(), click(), send_keys(). El desafío es dominar esperas, elegir localizadores estables y estructurar tests con Page Object Model. Practica con una aplicación real, no solo tutoriales.

¿Qué lenguaje es mejor para Selenium?

Python para principiantes — sintaxis simple, feedback rápido, excelente documentación. Java para proyectos enterprise con infraestructura Java existente. JavaScript si tu equipo ya usa Node.js. Todos los lenguajes tienen soporte maduro de Selenium.

¿Sigue siendo relevante Selenium en 2026?

Sí. Selenium sigue siendo el estándar de la industria con la comunidad más grande, más tutoriales y el soporte más amplio de navegadores. Aunque Playwright y Cypress ofrecen mejor experiencia de desarrollador, Selenium se integra con más herramientas y tiene mayor adopción enterprise.

¿Cuál es la diferencia entre Selenium y Playwright?

Selenium controla navegadores mediante protocolo WebDriver (estándar W3C). Playwright usa Chrome DevTools Protocol para navegadores Chromium. Playwright tiene mejor auto-wait, assertions incorporados, trace viewer y ejecución paralela. Selenium tiene soporte más amplio de navegadores y más recursos de aprendizaje. Para proyectos nuevos enfocados en Chromium, Playwright suele ser mejor. Para proyectos existentes o necesidades Safari/legacy, Selenium sigue siendo sólido.

Cuándo Elegir Selenium

Elige Selenium cuando:

  • El equipo tiene experiencia existente en Selenium
  • Necesitas soporte Safari o navegadores legacy
  • Entorno corporativo con infraestructura Selenium
  • Quieres máximo soporte de comunidad
  • Usas Appium para móvil (misma API)

Considera alternativas cuando:

  • Empiezas de cero con foco en Chromium (Playwright)
  • Testing de componentes con framework JavaScript (Cypress)
  • Prefieres auto-wait y API más simple (Playwright)
  • Necesitas grabación de video y trace debugging (Playwright)

Recursos Oficiales

Ver También