¿Qué es Playwright?
Playwright es un framework de pruebas end-to-end multiplataforma de Microsoft. Controla Chromium, Firefox y WebKit a través de una única API, espera automáticamente a que los elementos estén listos y ofrece un test runner con fixtures, paralelismo, tracing y control de red.
Surgió a partir de Puppeteer y conservó sus mejores aspectos —un protocolo moderno y una API limpia— al tiempo que añadió soporte multiplataforma real y herramientas de primer nivel. Para los equipos que comienzan una suite de pruebas end-to-end hoy en día, es la opción predeterminada más común, y su auto-waiting es la razón principal por la cual sus pruebas son menos inestables (flaky) que las de herramientas más antiguas.
Escribiendo una prueba
Una prueba navega, interactúa y realiza aserciones. Playwright proporciona las funciones test y expect y un fixture page.
// tests/login.spec.ts
import { test, expect } from "@playwright/test";
test("user can sign in", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("[email protected]");
await page.getByLabel("Password").fill("secret");
await page.getByRole("button", { name: "Sign in" }).click();
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
});
El fixture page es una pestaña del navegador. Acciones como click y fill esperan a que el elemento esté disponible para interactuar antes de actuar, por lo que no es necesario insertar esperas (sleep) ni hay riesgo de condiciones de carrera.
Locators y auto-waiting
Los locators son consultas perezosas (lazy queries) que se resuelven en un elemento al momento de usarse, y reintentan la operación hasta que el elemento esté listo. Prioriza los locators orientados al usuario en este orden:
- getByRole — botones, enlaces, encabezados e inputs mediante su rol accesible y nombre.
- getByLabel — controles de formulario mediante su etiqueta.
- getByText — texto visible.
- getByPlaceholder, getByAltText, getByTitle — otros atributos visibles.
- getByTestId — un id de prueba estable cuando ninguna opción orientada al usuario sea adecuada.
// locators.ts
page.getByRole("link", { name: "Pricing" });
page.getByLabel("Search");
page.getByText("Welcome back");
page.getByTestId("cart-total");
Debido a que las acciones cuentan con auto-wait, rara vez necesitarás esperas explícitas. Las aserciones también están diseñadas para la web: expect(locator).toBeVisible() reintenta hasta que se cumpla la condición o expire el tiempo de espera (timeout), lo que hace que las pruebas sean resilientes a los problemas de timing.
// assertions.ts
await expect(page.getByRole("alert")).toHaveText("Saved");
await expect(page.getByRole("button", { name: "Save" })).toBeEnabled();
await expect(page.getByTestId("row")).toHaveCount(3);
Fixtures y configuración compartida
El sistema de fixtures de Playwright es la forma de compartir la configuración sin tener que repetirla. Una fixture es un valor inyectado en las pruebas, el cual es creado y destruido por el runner.
// fixtures.ts
import { test as base } from "@playwright/test";
export const test = base.extend({
authenticatedPage: async ({ page }, use) => {
await page.goto("/login");
await page.getByLabel("Email").fill("[email protected]");
await page.getByLabel("Password").fill("secret");
await page.getByRole("button", { name: "Sign in" }).click();
await use(page);
},
});
Específicamente para la autenticación, el patrón recomendado es utilizar un proyecto de setup que inicie sesión una sola vez, guarde el storage state en un archivo y lo reutilice en todas las pruebas. Esto mantiene la suite rápida y evita repetir el flujo de login.
Control de red
page.route intercepta las solicitudes, lo que te permite probar estados de carga, errores y casos límite sin necesidad de un backend real.
// mock.ts
await page.route("**/api/users", (route) =>
route.fulfill({
status: 500,
body: JSON.stringify({ message: "Server error" }),
}),
);
await page.goto("/users");
await expect(page.getByRole("alert")).toContainText("went wrong");
También puedes realizar aserciones sobre lo que la aplicación envió, retrasar las respuestas para probar los estados de carga o bloquear scripts de terceros para mantener las pruebas rápidas y deterministas.
Depuración y herramientas
Las herramientas de Playwright son una gran parte de su atractivo:
- Codegen graba tus interacciones y genera un archivo de prueba con locators.
- UI mode ejecuta las pruebas en una interfaz de watch donde puedes avanzar paso a paso por las acciones e inspeccionar el DOM.
- Trace viewer graba un trazo completo —capturas de pantalla, snapshots del DOM, red y consola— que puedes reproducir después de un fallo en CI.
- Reporters generan reportes en HTML, JUnit y otros formatos de forma nativa.
El trace viewer, en particular, convierte un fallo misterioso en CI en una reproducción paso a paso, lo que a menudo marca la diferencia entre una corrección de cinco minutos y una tarde entera de suposiciones.
Paralelismo y CI
Por defecto, Playwright Test ejecuta los archivos de prueba en paralelo a través de procesos worker. Puedes ejecutar la misma suite en diferentes proyectos de navegador, ajustar el número de workers y fragmentar (shard) una suite extensa entre varias máquinas con --shard. Para paralelizar de forma segura, las pruebas deben ser independientes y evitar el estado mutable compartido.
En CI, instala los navegadores con npx playwright install --with-deps, ejecuta la suite y sube el reporte HTML y los traces como artefactos. Los reintentos (retries) están disponibles para infraestructuras genuinamente inestables, pero deben ser una red de seguridad y no un sustituto de la corrección de la inestabilidad (flakiness).
Dónde encaja Playwright
Las pruebas end-to-end están en la cima de la pirámide: son pocas, lentas y de alto valor. Debajo de ellas se encuentran las pruebas de componentes con Testing Library y las rápidas pruebas unitarias con Vitest o Jest. Playwright cubre los flujos que solo un navegador real puede verificar: autenticación, checkout, navegación y el comportamiento cross-browser.
Mejores prácticas
- Prioriza los localizadores de
roleylabel; usa lostest idscon moderación. - Confía en el auto-waiting en lugar de
waitForTimeout. - Mantén los tests independientes para que se puedan ejecutar en paralelo de forma segura.
- Reutiliza la autenticación mediante el
storage stateen lugar de iniciar sesión en cada prueba. - Haz mock de la red para los casos borde; mantén algunos tests contra el backend real.
- Registra trazas (
traces) cuando haya fallos y súbelas a la CI. - Usa
data-testidúnicamente para elementos que no tengan una identidad accesible.
Errores comunes
- Usar selectores de CSS o XPath vinculados al estilo y a la estructura.
- Implementar esperas fijas con
waitForTimeoutque ralentizan la suite y ocultan condiciones de carrera (race conditions). - Compartir el estado entre tests, rompiendo el paralelismo.
- Probar cada caso borde de extremo a extremo (end-to-end) en lugar de bajarlos en la pirámide de pruebas.
- Ignorar los traces e intentar adivinar la causa de los fallos en CI.
- Depender de los reintentos (retries) para maquillar la inestabilidad (flakiness) real de los tests.
Próximos pasos
Playwright es actualmente la opción más sólida por defecto para las pruebas end-to-end. Compáralo con Cypress para conocer una experiencia de desarrollo alternativa, y construye las capas inferiores con Vitest y Testing Library. Después, añade un flujo crítico del usuario como tu primera prueba e2e y ve escalando a partir de ahí.