Pruebas de Regresión Visual con Playwright: Detectando Cambios en la UI Automáticamente

Serie: Calidad y Accesibilidad en Aplicaciones Web — Artículo 1 de 3
Introducción
Cuando probamos aplicaciones web, solemos enfocarnos en la funcionalidad — pero ¿cómo garantizamos que la interfaz de usuario se vea correcta después de cada despliegue?
Ahí es donde entran las pruebas visuales.
Las pruebas visuales comparan automáticamente capturas de pantalla de la UI entre ejecuciones, ayudándonos a detectar cambios inesperados en el diseño, elementos faltantes o regresiones de estilos. Un cambio de padding, un botón que se achicó, un componente que se desplazó dos píxeles — ese tipo de cosas que pasan desapercibidas en code review pero que el equipo de UX y QA necesita detectar antes de que lleguen a producción.
Aunque existen herramientas dedicadas como Percy de BrowserStack que ofrecen capacidades avanzadas de comparación visual, tienen un límite de screenshots. La funcionalidad nativa de Playwright no tiene ese costo directo — los snapshots viven en tu repositorio. El "costo" real es el tamaño en disco y en tu historial de Git, algo que puedes gestionar con una estrategia de almacenamiento adecuada. Para equipos y clientes que priorizan herramientas open source, esto es un argumento sólido: obtienes comparación visual, evidencia histórica, soporte multi-browser y multi-breakpoint, todo dentro de la misma herramienta que ya usas para tus pruebas funcionales.
En este artículo voy a mostrarte cómo configurar pruebas de regresión visual con Playwright usando ejemplos reales de AutoCatalog, un catálogo de autos que construí en Next.js + TypeScript para experimentar con estos patrones.
Al finalizar, podrás:
Capturar capturas base (baselines) de los componentes clave de la UI
Compararlas automáticamente en futuras ejecuciones
Detectar cambios visuales no deseados de forma temprana en tu pipeline de CI
⚙️ Prerrequisitos
Antes de comenzar, asegúrate de contar con:
Node.js (versión LTS recomendada)
Un proyecto de Playwright ya configurado:
pnpm create playwright
- Conocimiento básico de pruebas en Playwright y del patrón Page Object Model (POM)
Configuración de pruebas visuales
1. Aserciones básicas de capturas de pantalla
Playwright incluye soporte nativo para comparaciones visuales mediante expect(page).toHaveScreenshot() o expect(element).toHaveScreenshot().
La primera vez que ejecutas una prueba, Playwright guarda una imagen base (baseline). En ejecuciones posteriores, compara la nueva captura con la base y señala cualquier diferencia.
Para ilustrar el valor real de esto: imagina que alguien modifica el tamaño o el padding del botón "Add Product" — un cambio pequeño que fácilmente pasa desapercibido en code review. La prueba de regresión visual lo detecta de inmediato.
test('Product Management - Default layout page', async ({ page }) => {
await test.step('Capture full-page baseline', async () => {
await expect(page).toHaveScreenshot('manage_default_layout.png')
})
})
Si el layout cambia, Playwright genera una imagen con las diferencias (diff) y el reporte incluye un slider interactivo para comparar el antes y el después:
![]()
📸 El slider del reporte de Playwright permite comparar visualmente el baseline con el estado actual — ideal para detectar cambios sutiles de spacing o tamaño.
2. Pruebas visuales a nivel de componente
En lugar de capturar páginas completas, puedes enfocarte en componentes específicos o modales. Esto hace que los tests sean más estables: un cambio en el header no rompe la prueba del modal.
test('Product Management - Add Product modal default layout matches baseline', async ({ page }) => {
await test.step('Open Add Product modal', async () => {
await managePage.openAddProductModal()
await managePage.expectAddProductModalVisible()
})
await test.step('Capture modal component screenshot', async () => {
const productModal = managePage.modalTitle.locator('..')
await expect(productModal).toHaveScreenshot(
'add_product_modal_default.png'
)
})
})
El scope del screenshot es el modal en sí — no la página completa. Así, la prueba verifica que el layout, espaciado y elementos del modal se mantengan consistentes sin depender del resto de la UI.
![]()
📸 Snapshot del modal Add Product — la prueba verifica que el layout, espaciado y elementos del modal se mantengan consistentes.
3. Captura de snapshots en estados clave de la UI
Las pruebas visuales son más efectivas cuando capturas estados relevantes de la interfaz, no cada interacción.
El carrusel de la home: el problema del contenido dinámico
Verificamos que el diseño de la home se vea correcto sin ruido generado por animaciones. El carrusel muestra imágenes en posiciones aleatorias en cada ejecución — sin mask, el test falla constantemente aunque el layout esté perfecto.
test('Home - Full page screenshot with carousel masked — stable test', async ({ page }) => {
await test.step('Wait for carousels to be visible', async () => {
await homePage.expectCarouselsVisible()
})
await test.step('Capture full page with all carousels masked', async () => {
const allCarousels = await homePage.getAllCarousels()
await expect(page).toHaveScreenshot('home_with_mask.png', {
mask: allCarousels,
})
})
})
👉 La opción
mask: Locator[]ignora las áreas que cambian dinámicamente, como el carrusel.
Veamos qué ocurre en ambos casos:
![]()
📸 Sin mask: el test falla porque el carrusel muestra imágenes diferentes entre ejecuciones — Playwright detecta las diferencias y las marca en rojo.
![]()
![]()
📸 Con mask aplicado: las áreas dinámicas son ignoradas y el test pasa de forma consistente.
4. Gestión de imágenes base
La primera vez que ejecutas las pruebas, Playwright guarda las capturas base en:
/tests/screenshots/
Si un cambio visual es intencional, actualiza los snapshots con:
pnpm exec playwright test --update-snapshots
⚠️ Nota para CI: Evita ejecutar
--update-snapshotsde forma automática en tu pipeline. Reserva este comando para actualizaciones manuales y revisadas. Considera crear un workflow separado o protegerlo detrás de una confirmación explícita.
Para revisar las diferencias:
Abre la carpeta
test-resultsCompara las imágenes: base, actual y diff
Aprueba o rechaza los cambios durante la revisión de código
Buenas prácticas
1. Organiza las pruebas por funcionalidad
Agrupa las pruebas visuales lógicamente usando test.describe():
test.describe('@ProductManagement @Visual', () => {
test.describe('@LayoutBaseline', () => {
// Prueba del layout completo
})
test.describe('@ModalBaseline', () => {
// Pruebas de componentes modales
})
})
2. Usa nombres descriptivos para los screenshots
Nombra las capturas de forma clara para indicar qué están probando:
✅ manage_default_layout.png
✅ add_product_modal_masked_dropdowns.png
❌ screenshot1.png
3. Prepara correctamente el estado de la prueba
Asegúrate de que la aplicación esté en el estado correcto antes de capturar una pantalla:
test.beforeEach(async ({ page }) => {
poManager = new POManager(page)
managePage = poManager.getManagePage()
await managePage.navigateToManage()
await managePage.expectManagePageLoaded()
})
4. Usa test.step con snapshots
Es posible tomar snapshots dentro de test.step, lo que permite capturar múltiples estados en un mismo test. La ventaja es que el test queda más cohesivo y el reporte de Playwright muestra exactamente qué paso falló. La contrapartida: si un step falla, los siguientes no se ejecutan — en algunos casos es mejor separar los tests.
test('Add Product modal — múltiples estados', async ({ page }) => {
await test.step('Open modal', async () => {
await managePage.openAddProductModal()
await managePage.expectAddProductModalVisible()
})
await test.step('Capture default state', async () => {
await expect(productModal).toHaveScreenshot('modal_default.png')
})
await test.step('Capture focus on submit button', async () => {
await managePage.activateTabKeyboard()
await managePage.focusModalSubmitButton()
await expect(productModal).toHaveScreenshot('modal_submit_focus.png')
})
})
5. Prueba diseños responsivos
Verifica que los layouts responsivos funcionen correctamente en distintos tamaños de pantalla:
test('Home - mobile layout', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 })
await expect(page).toHaveScreenshot('home-mobile.png')
})
🧠 Solución de problemas frecuentes
| Problema | Solución |
|---|---|
| Diferencias mínimas de píxeles entre ejecuciones | Diferencias mínimas de píxeles entre ejecuciones → Identifica la causa raíz: usa mask para contenido dinámico, addStyleTag para scrollbars, o espera explícita para elementos que aún están renderizando. Evita maxDiffPixelRatio — acepta píxeles incorrectos como válidos y puede ocultar regresiones reales. |
| Contenido dinámico (carruseles, timestamps, animaciones) | Usa la opción mask para excluirlo del snapshot |
| Falsos positivos en CI | Asegura resolución de pantalla y versión del navegador consistentes entre entornos |
| Scrollbars que generan diffs | Ocúltalos con addStyleTag antes del screenshot |
⚠️ Sobre
maxDiffPixelRatio: Aunque Playwright ofrece esta opción para tolerar diferencias mínimas de píxeles, úsala con precaución. Un valor de0.01puede parecer pequeño, pero en una pantalla de 1280×720 equivale a más de 9.000 píxeles — suficiente para ocultar un botón desalineado o un cambio de color. En la mayoría de los casos, la solución correcta es eliminar la fuente de inestabilidad, no tolerarla.
🎯 Conclusión
Al combinar las pruebas funcionales de Playwright con aserciones visuales, puedes detectar y prevenir con confianza cambios no deseados en la UI.
Este enfoque le da a tu equipo de QA y desarrollo una capa de seguridad adicional: garantizando que tanto el comportamiento como la apariencia de la aplicación se mantengan consistentes con el tiempo.
Puntos clave:
Usa
toHaveScreenshot()para capturar y comparar estados de la UIEnmascara elementos dinámicos para obtener diffs confiables
Versiona las imágenes base y revisa los cambios a través del proceso de revisión de código
Evita actualizar snapshots automáticamente en CI sin una revisión explícita
Usa
test.steppara organizar tests con múltiples capturas y obtener reportes más granulares
Este es el primero de una serie sobre QA automation — próximamente:
Automatizando Pruebas de Accesibilidad Web: Combinando axe-core y Playwright
WCAG 2.4.7 Foco Visible: Pruebas de Regresión Visual con Playwright
¿Tu equipo tiene cobertura de regresión visual? Si quieres explorar cómo implementar este tipo de testing en tu proyecto, cuéntame sobre tu equipo aquí.
