El roadmap definitivo con ejemplos prácticos,
arquitecturas reales y código production-ready
Por Hugo Napa Rojas
Head de QA de WIRBI
+1M tests automatizados/mes
Desliza o usa las flechas para navegar
Por qué este es el momento perfecto para automatizar
Comparación honesta con código real
1. Abrir Chrome
2. Navegar a app.com
3. Click en "Login"
4. Escribir email
5. Escribir password
6. Click "Submit"
7. Verificar dashboard
8. Screenshot
9. Documentar
⏱️ 3-5 minutos
🔄 Manual
❌ No CI/CD
def test_login(driver):
page = LoginPage(driver)
dash = Dashboard(driver)
page.navigate()
page.login(
email="test@test.com",
password="Test123!"
)
assert dash.is_displayed()
⏱️ 5 segundos
🔄 ∞ veces
✅ CI/CD ready
ROI: 1 test = 15 horas/mes ahorradas
Estrategia de automatización que funciona
Cuándo: Lógica
Tiempo: < 1ms
Cuándo: APIs
Tiempo: 10-100ms
Cuándo: Flujos
Tiempo: 5-30s
De 0 a test funcional en 10 minutos
# pip install selenium pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
import pytest
class TestGoogle:
def setup_method(self):
self.driver = webdriver.Chrome()
self.driver.maximize_window()
def teardown_method(self):
self.driver.quit()
def test_search(self):
# Navigate
self.driver.get("https://google.com")
# Search
search = self.driver.find_element(
By.NAME, "q"
)
search.send_keys("Selenium")
search.submit()
# Verify
assert "Selenium" in self.driver.title
# Run: pytest test_google.py -v
La arquitectura que escala
class LoginPage:
def __init__(self, driver):
self.driver = driver
# Locators
self.EMAIL = (By.ID, "email")
self.PASSWORD = (By.ID, "password")
self.LOGIN_BTN = (By.ID, "login-btn")
def navigate(self):
self.driver.get(Config.BASE_URL)
return self
def enter_email(self, email):
element = self.driver.find_element(*self.EMAIL)
element.clear()
element.send_keys(email)
return self
def login(self, email, password):
return (self.enter_email(email)
.enter_password(password)
.click_login())
def test_valid_login(driver):
# Arrange
login_page = LoginPage(driver)
# Act
dashboard = login_page.navigate().login(
email="test@test.com",
password="Test123!"
)
# Assert
assert dashboard.is_displayed()
Por qué el 70% de tests deberían ser API
import requests
import pytest
class TestUserAPI:
BASE_URL = "https://api.example.com/v1"
@pytest.fixture
def auth_headers(self):
response = requests.post(
f"{self.BASE_URL}/auth/login",
json={
"email": "test@test.com",
"password": "Test123!"
}
)
token = response.json()['token']
return {
'Authorization': f'Bearer {token}'
}
def test_create_user(self, auth_headers):
user_data = {
"name": "John Doe",
"email": "john@example.com"
}
response = requests.post(
f"{self.BASE_URL}/users",
json=user_data,
headers=auth_headers
)
assert response.status_code == 201
assert 'id' in response.json()
GitHub Actions en acción
name: Test Automation
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
playwright install chrome
- name: Run tests
run: |
pytest tests/ \
--browser=chrome \
--parallel=4 \
--alluredir=allure-results
- name: Generate Report
if: always()
run: |
allure generate allure-results
- name: Upload results
uses: actions/upload-artifact@v3
with:
name: test-results
path: allure-report/
Resultado: Tests automáticos en cada commit
9 ejecuciones paralelas en 5 minutos
El ecosistema actual de testing
Categoría | Herramienta | Por qué |
---|---|---|
🌐 Web | Playwright | Auto-wait, multi-browser |
⚡ JS | Cypress | Time travel, debugging |
📱 Mobile | Maestro | Más simple que Appium |
🤖 Visual | Percy | AI para cambios visuales |
⚡ Perf | K6 | Tests as code |
🔒 Security | OWASP ZAP | Security automatizado |
☁️ Cloud | BrowserStack | Real devices |
🤖 AI | Testim.io | Self-healing tests |
Principiante: Cypress o Playwright
Intermedio: + K6 + Percy
Avanzado: + Pact + Stryker
Cómo medir el éxito
def calculate_roi():
manual_time = 5 # minutos
automated_time = 0.1
executions = 100
time_saved = (manual_time - automated_time) * executions
hourly_rate = 50 # USD
monthly_savings = (time_saved / 60) * hourly_rate
return {
'hours_saved': time_saved / 60,
'cost_saved': monthly_savings
}
Elige tu camino
✓ 100+ tests en producción
✓ Pipeline CI/CD
✓ Portfolio GitHub
✓ Certificación
Demuestra tus skills
Stack: Playwright + TS
Stack: Python + Pytest
Stack: Appium + Java
Stack: K6 + Grafana
Stack: React + Node.js
Dashboard con resultados real-time, histórico y métricas
Lo que NO debes hacer
# Sleep hardcoded
time.sleep(10)
# Tests dependientes
def test_1_create():
global user_id
user_id = create()
# Assert débil
assert response
# Hardcoded
driver.get("localhost:3000")
# Sin cleanup
create_resource()
# No delete!
# Explicit wait
wait.until(EC.element_to_be_clickable)
# Tests independientes
def test_lifecycle():
user = create()
try:
verify(user)
finally:
delete(user)
# Assert específico
assert response.status == 200
# Config externa
driver.get(Config.BASE_URL)
# Con fixture
@pytest.fixture
def user():
u = create()
yield u
delete(u)
IA y más allá
# Prompt: "Generate test for registration"
class TestRegistration:
@pytest.mark.parametrize("scenario,data,expected", [
# IA genera estos casos:
("valid", valid_data(), 201),
("duplicate", dup_email(), 409),
("sql_injection", sql_attack(), 400),
("xss", xss_attempt(), 400),
("unicode", unicode_data(), 201)
])
def test_scenarios(self, scenario, data, expected):
response = self.api.register(data)
assert response.status_code == expected
2026: 50% tests generados por IA
Tu rol: Arquitecto, no escribidor
En 6 meses: Automation Engineer
En 12 meses: Experto
En 24 meses: Líder técnico
1️⃣ Instala Python y VS Code
2️⃣ Escribe tu primer test
3️⃣ Súbelo a GitHub
4️⃣ Compártelo en LinkedIn
5️⃣ Únete a la comunidad
Hugo Napa Rojas
Head de QA de WIRBI
qa@wirbi.com