Introdução ao Selenium WebDriver
Selenium WebDriver é a ferramenta mais popular para automação de testes web. Permite controlar browsers programaticamente para executar ações e verificar resultados.
Arquitetura
┌─────────────┐ JSON Wire Protocol ┌─────────────┐
│ Tests │ ←──────────────────────→ │ Browser │
│ (Python) │ │ (Chrome) │
└─────────────┘ └─────────────┘
↑ ↑
│ │
└────────── WebDriver API ───────────────┘
Setup
Instalação
# Python
pip install selenium webdriver-manager
# Java
org.seleniumhq.selenium
selenium-java
4.15.0
# JavaScript
npm install selenium-webdriver
Setup Rápido
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
# Setup automático
driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install())
)
driver.get("https://example.com")
print(driver.title)
driver.quit()
Locators
Priority de Locators
| Prioridade | Locator | Exemplo | Confiabilidade |
|---|---|---|---|
| 1 | ID | By.ID, "username" |
Mais confiável |
| 2 | Name | By.NAME, "email" |
Bom |
| 3 | Data Test ID | By.CSS, "[data-testid='btn']" |
Excelente |
| 4 | Class | By.CLASS_NAME, "btn-primary" |
Médio |
| 5 | XPath | By.XPATH, "//button[@id='submit']" |
Flexível |
| 6 | CSS | By.CSS_SELECTOR, "#submit" |
Rápido |
| 7 | Link Text | By.LINK_TEXT, "Click here" |
Texto âncora |
| 8 | Partial Link | By.PARTIAL_LINK_TEXT, "here" |
Texto parcial |
Exemplos de Locators
from selenium.webdriver.common.by import By
# By ID (mais rápido)
driver.find_element(By.ID, "username")
driver.find_element(By.ID, "password")
driver.find_element(By.ID, "login-button")
# By Name
driver.find_element(By.NAME, "email")
driver.find_element(By.NAME, "search")
# By XPath (absoluto - evitar)
driver.find_element(By.XPATH, "/html/body/div[2]/form/input[1]")
# XPath relativo (preferir)
driver.find_element(By.XPATH, "//input[@id='username']")
driver.find_element(By.XPATH, "//button[contains(text(), 'Login')]")
driver.find_element(By.XPATH, "//div[@class='form']//input[@name='email']")
# XPath com múltiplas condições
driver.find_element(By.XPATH,
"//button[@type='submit' and contains(@class, 'btn-primary')]")
# CSS Selector
driver.find_element(By.CSS_SELECTOR, "#username")
driver.find_element(By.CSS_SELECTOR, ".form-group .form-control")
driver.find_element(By.CSS_SELECTOR, "input[name='email']")
driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
# Link Text
driver.find_element(By.LINK_TEXT, "Forgot Password?")
driver.find_element(By.PARTIAL_LINK_TEXT, "Forgot")
# Data Test ID (recomendado para testes)
driver.find_element(By.CSS_SELECTOR, "[data-testid='login-button']")
###Locator Strategies
# Hierarquia de preferências
LOCATOR_STRATEGY = [
("data-testid", lambda x: By.CSS_SELECTOR, f"[data-testid='{x}']"),
("id", lambda x: By.ID, x),
("name", lambda x: By.NAME, x),
("aria-label", lambda x: By.CSS_SELECTOR, f"[aria-label='{x}']"),
("text", lambda x: By.XPATH, f"//*[contains(text(), '{x}')]"),
("css", lambda x: By.CSS_SELECTOR, x),
]
def find_element(driver, locator_type, locator_value):
"""Encontra elemento usando estratégia otimizada"""
by, value = LOCATOR_STRATEGYlocator_type
return driver.find_element(by, value)
Interações Básicas
# Navegação
driver.get("https://example.com")
driver.back()
driver.forward()
driver.refresh()
driver.title # Page title
driver.current_url # Current URL
# Localizar e interagir
element = driver.find_element(By.ID, "username")
element.send_keys("user@example.com")
element.clear()
# Click
driver.find_element(By.ID, "submit-button").click()
# Dropdowns
from selenium.webdriver.support.ui import Select
dropdown = Select(driver.find_element(By.ID, "country"))
dropdown.select_by_visible_text("Brazil")
dropdown.select_by_value("br")
dropdown.select_by_index(1)
# Checkboxes
checkbox = driver.find_element(By.ID, "accept-terms")
if not checkbox.is_selected():
checkbox.click()
# Radio buttons
radio = driver.find_element(By.CSS_SELECTOR, "input[name='plan'][value='premium']")
if not radio.is_selected():
radio.click()
Waits
Wait Implícito vs Explícito
# ❌ NÃO USE: Wait implícito (global, aplicado a todos)
driver.implicitly_wait(10) # Espera 10s para cada elemento
# ✅ USE: Wait explícito (específico por situação)
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Espera elemento ficar clicável
button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "submit"))
)
button.click()
Expected Conditions
from selenium.webdriver.support import expected_conditions as EC
# Elemento presente no DOM
EC.presence_of_element_located((By.ID, "username"))
# Elemento visível
EC.visibility_of_element_located((By.ID, "welcome-message"))
# Elemento clicável
EC.element_to_be_clickable((By.ID, "submit-button"))
# Texto presente
EC.text_to_be_present_in_element((By.CLASS_NAME, "message"), "Success")
# URL contém
EC.url_contains("dashboard")
# Título contém
EC.title_contains("Dashboard")
# Nova janela abre
EC.new_window_is_opened(original_handles)
# Alerta presente
EC.alert_is_present()
# Loading spinners disappear
EC.invisibility_of_element_located((By.CLASS_NAME, "spinner"))
Wait Customizado
# Wait para elemento com texto específico
def wait_for_text(driver, locator, text, timeout=10):
"""Espera até elemento conter texto específico"""
return WebDriverWait(driver, timeout).until(
EC.text_to_be_present_in_element(locator, text)
)
# Wait para página carregar completamente
def wait_for_page_load(driver, timeout=30):
"""Espera documento.readyState ser 'complete'"""
def document_ready(driver):
return driver.execute_script("return document.readyState") == "complete"
WebDriverWait(driver, timeout).until(document_ready)
# Wait para requisição AJAX
def wait_for_ajax(driver, timeout=10):
"""Espera requisições AJAX terminarem"""
def ajax_complete(driver):
return driver.execute_script(
"return jQuery.active == 0"
)
WebDriverWait(driver, timeout).until(ajax_complete)
Page Object Pattern
# pages/base_page.py
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BasePage:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def _find(self, locator):
return self.wait.until(
EC.presence_of_element_located(locator)
)
def _click(self, locator):
self.wait.until(
EC.element_to_be_clickable(locator)
).click()
def _input(self, locator, text):
element = self.wait.until(
EC.presence_of_element_located(locator)
)
element.clear()
element.send_keys(text)
def _get_text(self, locator):
return self.wait.until(
EC.visibility_of_element_located(locator)
).text
def is_displayed(self, locator):
try:
return self.wait.until(
EC.visibility_of_element_located(locator)
).is_displayed()
except:
return False
# pages/login_page.py
from selenium.webdriver.common.by import By
from .base_page import BasePage
class LoginPage(BasePage):
URL = "https://example.com/login"
# Locators
EMAIL_INPUT = (By.ID, "email")
PASSWORD_INPUT = (By.ID, "password")
LOGIN_BUTTON = (By.ID, "login-button")
ERROR_MESSAGE = (By.CLASS_NAME, "error-message")
FORGOT_PASSWORD_LINK = (By.LINK_TEXT, "Forgot Password?")
def load(self):
self.driver.get(self.URL)
return self
def login(self, email, password):
self._input(self.EMAIL_INPUT, email)
self._input(self.PASSWORD_INPUT, password)
self._click(self.LOGIN_BUTTON)
from pages.dashboard_page import DashboardPage
return DashboardPage(self.driver)
def get_error_message(self):
return self._get_text(self.ERROR_MESSAGE)
def click_forgot_password(self):
self._click(self.FORGOT_PASSWORD_LINK)
from pages.forgot_password_page import ForgotPasswordPage
return ForgotPasswordPage(self.driver)
# pages/dashboard_page.py
from .base_page import BasePage
from selenium.webdriver.common.by import By
class DashboardPage(BasePage):
WELCOME_MESSAGE = (By.CLASS_NAME, "welcome-message")
USER_NAME = (By.CSS_SELECTOR, "[data-testid='user-name']")
LOGOUT_BUTTON = (By.ID, "logout-button")
def get_welcome_message(self):
return self._get_text(self.WELCOME_MESSAGE)
def get_user_name(self):
return self._get_text(self.USER_NAME)
def logout(self):
self._click(self.LOGOUT_BUTTON)
from pages.login_page import LoginPage
return LoginPage(self.driver)
Testando Exemplos
# tests/test_login.py
import pytest
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage
class TestLogin:
@pytest.fixture(autouse=True)
def setup(self, driver):
self.driver = driver
self.login_page = LoginPage(driver)
def test_login_with_valid_credentials(self):
"""Deve fazer login e ir para dashboard"""
dashboard = self.login_page.load().login(
email="user@example.com",
password="password123"
)
assert dashboard.get_welcome_message() == "Welcome!"
assert dashboard.get_user_name() == "John Doe"
def test_login_with_invalid_credentials(self):
"""Deve exibir mensagem de erro"""
self.login_page.load()
self.login_page.login(
email="invalid@example.com",
password="wrongpassword"
)
assert "Invalid credentials" in self.login_page.get_error_message()
def test_forgot_password_flow(self):
"""Deve navegar para recuperação de senha"""
forgot_page = self.login_page.load().click_forgot_password()
assert forgot_page.is_displayed()
assert "Reset Password" in forgot_page.get_title()
Headless Mode
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-gpu")
driver = webdriver.Chrome(options=options)
Conclusão
Selenium WebDriver é poderoso para automação web. As chaves são:
- Use locators estáveis – data-testid > ID > XPath
- Waits explícitos – Sempre prefira sobre implícitos
- Page Objects – Separe locators e ações
- Evite sleeps – Use expected conditions
- Debug com screenshots – Capture estado em falhas
