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)
| Estrategia | Ejemplo | Cuándo Usar |
|---|---|---|
| ID | #login-button | IDs únicos (mejor opción) |
| Name | [name="email"] | Inputs de formularios |
| CSS Selector | .btn.primary | Mayoría de elementos |
| Link Text | Sign In | Links 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
| Tipo | Cómo Funciona | Cuándo Usar |
|---|---|---|
| Implicit | Consulta DOM por N segundos | Nunca (global, oculta problemas) |
| Explicit | Espera condición específica | Siempre (preciso, legible) |
| Fluent | Explicit + polling personalizado | Elementos 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
Navegador Headless
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
- Playwright: Guía Completa - Alternativa moderna con mejor experiencia de desarrollador
- Cypress Tutorial - Testing E2E enfocado en JavaScript
- Selenium WebDriver 2025 - Profundización en características avanzadas de Selenium
- TestNG vs JUnit5 - Comparación de frameworks de test para proyectos Java Selenium
