Para automatizar o probar un navegador con Python, instala Playwright y su binario, abre una página e interactúa mediante locators. La biblioteca espera automáticamente muchas condiciones necesarias para cada acción, mientras que expect comprueba estados visibles con reintentos. Descargas, capturas, ventanas nuevas y respuestas de red tienen API específicas. Junto con pytest, este modelo evita coordenadas, rutas DOM frágiles y pausas arbitrarias.
Playwright controla Chromium, Firefox y WebKit con una API coherente. Sirve para pruebas de extremo a extremo, regresión y tareas internas autorizadas. Puedes repasar pytest para pruebas en Python y comparar con la automatización general de tareas cuando no haga falta un navegador.
Instalación y primer navegador
Crea un entorno virtual de Python e instala paquetes y navegador:
python -m pip install playwright pytest pytest-playwright
python -m playwright install chromium
El segundo comando descarga una revisión compatible. En CI Linux, python -m playwright install --with-deps chromium puede agregar bibliotecas del sistema. Consulta la documentación oficial de Playwright para Python para instrucciones actuales.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com", wait_until="domcontentloaded")
print(page.title())
page.screenshot(path="evidencia.png", full_page=True)
browser.close()
La API síncrona es sencilla para scripts y pytest. Si el programa ya trabaja con asyncio, elige async_playwright y usa await de forma consistente. La guía de async y await en Python explica ese modelo. Mezclar ambas API complica el control de recursos.
Locators estables y expresivos
Un locator describe cómo encontrar un elemento y se resuelve de nuevo al actuar. Es preferible seleccionar por significado visible o por un contrato explícito:
page.get_by_role("textbox", name="Correo").fill("[email protected]")
page.get_by_label("Contraseña").fill("secreto-de-prueba")
page.get_by_role("button", name="Entrar").click()
page.get_by_test_id("menu-perfil").click()
get_by_role refleja semántica accesible. Las etiquetas, los placeholders y el texto visible también expresan intención. data-testid es apropiado cuando el texto cambia según idioma o no existe un rol adecuado. CSS sigue disponible, pero clases generadas y rutas como div:nth-child(4) acoplan la prueba a la presentación.
Los locators son estrictos cuando una acción exige un solo objetivo. Si coinciden dos botones, Playwright falla en lugar de elegir silenciosamente. Reduce el ámbito con un contenedor significativo, filter o locators encadenados. No uses .first solamente para ocultar una ambigüedad.
Esperar resultados, no segundos
Antes de hacer clic, Playwright verifica visibilidad, estabilidad, habilitación y recepción de eventos. Este auto-waiting elimina casi todos los time.sleep. Para el resultado, emplea aserciones con reintentos:
from playwright.sync_api import expect
page.get_by_role("button", name="Guardar").click()
expect(page.get_by_role("status")).to_have_text("Guardado")
expect(page).to_have_url("**/cuenta")
Espera el evento relevante. Usa page.expect_response alrededor de la acción si importa una respuesta concreta, o page.expect_popup para una pestaña nueva. No conviertas wait_for_load_state("networkidle") en solución universal: telemetría y conexiones persistentes pueden impedir la inactividad. Una aserción del estado final representa mejor el requisito.
Timeouts globales enormes ocultan defectos y vuelven lentos los fallos. Mantén un valor razonable y amplíalo solo para operaciones justificadas. Una prueba intermitente necesita diagnóstico, no más espera.
Descargas y capturas de pantalla
Escucha el evento antes del clic para no perder una descarga rápida:
from pathlib import Path
with page.expect_download() as info:
page.get_by_role("link", name="Descargar informe").click()
download = info.value
destino = Path("artifacts") / download.suggested_filename
destino.parent.mkdir(exist_ok=True)
download.save_as(destino)
assert destino.stat().st_size > 0
En pytest conviene utilizar un directorio temporal y comprobar contenido, extensión o encabezados, no solo existencia. Considera el nombre sugerido como entrada no confiable si el archivo saldrá del espacio aislado.
Las capturas ayudan a investigar, pero no reemplazan aserciones:
page.screenshot(path="fallo.png", full_page=True)
page.get_by_test_id("resumen").screenshot(path="resumen.png")
Imágenes y trazas pueden exponer contraseñas, tokens, cookies o datos personales. Usa cuentas sintéticas, evita producción y limita la conservación de artifacts de CI.
Integración con pytest
El plugin pytest-playwright ofrece una fixture page nueva y limpia recursos:
from playwright.sync_api import Page, expect
def test_pagina_muestra_titulo(page: Page) -> None:
page.goto("https://example.com")
expect(page.get_by_role("heading", name="Example Domain")).to_be_visible()
Ejecuta pytest --browser chromium. En suites mayores configura URL base, contexto, capturas y tracing de forma central. Cada prueba debe ser independiente. storage_state permite reutilizar autenticación, pero contiene credenciales y no debe entrar en Git. El artículo de pruebas automatizadas con pytest desarrolla fixtures y parametrización.
Prueba un comportamiento observable por caso. Un escenario que crea, edita, exporta y elimina resulta difícil de diagnosticar. Prepara datos mediante una API autorizada cuando sea posible y reserva la interfaz para aquello que realmente deba verificarse en el navegador.
Uso ético y seguro
Acceso técnico no significa permiso. Automatiza sistemas propios o autorizados, revisa términos, privacidad y legislación, y limita el tráfico. No evites CAPTCHA, autenticación, paywalls, límites ni controles de acceso. Si una API documentada resuelve la necesidad, normalmente es la opción más estable.
Emplea cuentas de prueba con privilegio mínimo, entornos separados y secretos administrados por CI. Limita concurrencia y detente ante errores repetidos para no cargar un servicio inestable. Recoge solo información necesaria y define cuándo eliminarla. Para extracción pública legítima, revisa la guía de web scraping con Python.
Controla el estado y reproduce los fallos
Cada contexto del navegador es una sesión aislada con sus propias cookies, almacenamiento local, permisos, idioma y viewport. Crea un contexto nuevo para escenarios independientes en lugar de borrar el estado manualmente. Cuando el flujo necesite autenticación previa, genera el storage_state mediante una preparación autorizada, protege el archivo resultante como una credencial y limita su vigencia. No compartas un contexto mutable entre pruebas paralelas, porque la navegación, los diálogos y los cambios de sesión pueden interferir entre sí.
Registra en la configuración las condiciones del entorno. Fija el idioma y la zona horaria cuando el formato afecte las aserciones, elige un viewport representativo y concede únicamente los permisos requeridos. Simular todas las respuestas puede producir una suite rápida pero incapaz de detectar defectos de integración. Reserva la interceptación de rutas para dependencias controladas y casos de error explícitos, manteniendo un conjunto menor de pruebas contra un entorno realista.
El tracing de Playwright registra acciones, instantáneas, actividad de red y ubicaciones del código. Activa las trazas para reintentos o fallos, en vez de conservar indefinidamente cada ejecución exitosa. La documentación oficial de Trace Viewer explica cómo inspeccionarlas. Trata estos archivos como datos sensibles, ya que pueden contener texto de páginas, URLs, cabeceras e información de sesión.
Errores frecuentes y checklist
- Sustituye pausas fijas por estados o eventos concretos.
- Selecciona por rol, etiqueta, texto estable o ID de prueba.
- Aísla página y datos mutables entre pruebas paralelas.
- Comprueba comportamiento; una captura no demuestra éxito.
- Valida las descargas dentro de un directorio temporal.
- Mantén sincronizados paquete y navegador en CI.
- Protege trazas, cookies, capturas y estado autenticado.
- Confirma autorización e impacto antes de ejecutar.
- Divide recorridos largos en casos atribuibles.
- Investiga la intermitencia en vez de ocultarla con reintentos.
Una suite confiable empieza con un recorrido crítico corto, locators semánticos, esperas por resultado y evidencia protegida. Ejecútala localmente y en CI, elimina la intermitencia y amplía la cobertura de forma deliberada.