Mejores prácticas de Playwright
Este capítulo resume las mejores prácticas más importantes en la escritura y el mantenimiento de pruebas de Playwright, para ayudarte a escribir pruebas estables y mantenibles.
Filosofía de pruebas
Probar el comportamiento visible para el usuario
Las pruebas automatizadas deben verificar lo que los usuarios finales pueden ver e interactuar, no los detalles de implementación del código.
Por ejemplo, las pruebas deben verificar que la página muestra el texto correcto, que los botones se pueden hacer clic y que el formulario redirige a la página correcta después del envío, en lugar de verificar el valor de retorno de una función de JavaScript o la estructura interna del DOM.
Ejemplo
expect(await page.evaluate(() => window.__store.getState().user.name))
.toBe('ejemplo');
// Recomendado: probar lo que el usuario ve
await expect(page.getByText('Bienvenido, ejemplo')).toBeVisible();
Aislamiento de pruebas
Cada prueba debe sercompletamente independientede otras pruebas, sin depender del estado de la prueba anterior.
Playwright proporciona un Context independiente por defecto, pero también debes asegurarte de no depender del orden de ejecución de las pruebas.
Ejemplo
let createdId;
test('Crear recurso', async ({ request }) => {
const resp = await request.post('/api/items');
createdId = (await resp.json()).id; // Estado compartido
});
test('Usar el recurso creado', async ({ page }) => {
await page.goto(`/items/${createdId}`); // Depende del resultado de la prueba anterior
});
// Recomendado: cada prueba es autosuficiente
test('Crear y usar recurso', async ({ request, page }) => {
const resp = await request.post('/api/items');
const id = (await resp.json()).id;
await page.goto(`/items/${id}`);
await expect(page.getByText('Detalles del recurso')).toBeVisible();
});
Principio de prioridad de los localizadores
Usar los localizadores en el orden recomendado puede mejorar la estabilidad de las pruebas.
| Prioridad | Método | Escenario aplicable |
|---|---|---|
| 1 | getByRole() | El elemento tiene un rol ARIA claro |
| 2 | getByLabel() | El elemento de formulario está asociado a una etiqueta (label) |
| 3 | getByPlaceholder() | El campo de entrada tiene placeholder |
| 4 | getByText() | El elemento tiene texto claro |
| 5 | getByAltText() | La imagen tiene atributo alt |
| 6 | getByTitle() | El elemento tiene atributo title |
| 7 | getByTestId() | Plan de respaldo |
| 8 | locator() | CSS/XPath, última opción |
Evita usar nombres de clases CSS como localizadores.
Los nombres de clases son propensos a cambiar debido a refactorizaciones de estilos, lo que provoca fallos masivos en las pruebas.
Evitar esperas innecesarias
Aprovecha el mecanismo de espera automática de Playwright, evita escribir manualmentesleepowaitForTimeout。
Ejemplo
await page.waitForTimeout(3000);
await page.getByText('Carga de datos completada').click();
// Recomendado: confiar en la espera automática y las aserciones
await expect(page.getByText('Cargando datos...')).toBeHidden({ timeout: 10000 });
await page.getByText('Carga de datos completada').click();
No probar servicios de terceros
Prueba solo lo que puedes controlar, no dependas de la disponibilidad de servicios de terceros.
Ejemplo
await page.goto('https://www.example.com/');
// La página puede cargar Google Analytics, fuentes externas, etc.
// Recomendado: interceptar solicitudes de terceros y simular (mock) servicios externos
await page.route('**/*analytics*', route => route.abort());
await page.route('**/external-api/**', route => {
route.fulfill({ json: { status: 'ok' } });
});
Usar aserciones suaves expect.soft()
Al verificar múltiples condiciones independientes, las aserciones suaves pueden informar todos los fallos de una vez, en lugar de detenerse en el primer fallo.
Ejemplo
await expect.soft(page.getByLabel('Nombre de usuario')).toBeVisible();
await expect.soft(page.getByLabel('Contraseña')).toBeVisible();
await expect.soft(page.getByLabel('Correo electrónico')).toBeVisible();
await expect.soft(page.getByRole('button', { name: 'Registrarse' })).toBeEnabled();
// Si faltan varios campos, se informan todos a la vez
Organizar bien los archivos de prueba
| Práctica | Descripción |
|---|---|
| Dividir archivos por módulo funcional | login.spec.ts、checkout.spec.tsesperar |
| Usar describe para agrupar | Coloca las pruebas relacionadas entest.describe()Medio |
| Usar bien beforeEach | Extrae los pasos repetidos de navegación y preparación abeforeEach |
| Nombres de prueba descriptivos | Un buen nombre explica "qué se hizo" y "qué se espera" |
| Extraer código común | Los helpers utilizados en más de 3 archivos se extraen a un módulo compartido |
| No exagerar con DRY | 2-3 líneas duplicadas son más fáciles de mantener que una abstracción excesiva |
Solución de problemas comunes
Pruebas inestables (flaky)
Causas y soluciones de las pruebas inestables:
| Causa | Solución |
|---|---|
| sleep codificado no es lo suficientemente largo | Usar el mecanismo de espera automática en lugar de esperas fijas |
| Depender de servicios de terceros | Usarroute()Simular (mock) solicitudes externas |
| Las animaciones cambian la posición de los elementos | En la configuraciónanimations: 'disabled' |
| Datos residuales entre pruebas | Asegúrate de que cada prueba cree y limpie sus datos de forma independiente |
| Lógica relacionada con el tiempo | Usarpage.clocktiempo fijo |
El localizador no encuentra el elemento
Pasos para solucionar:
- Confirma si el elemento está en el DOM (usando
toBeAttached()en lugar detoBeVisible()) - Confirma si el elemento está dentro del viewport
- Confirma si está anidado en un iframe
- En UI Mode, usa Pick Locator para verificar el localizador
- Comprueba si hay múltiples elementos coincidentes (Locator requiere modo estricto por defecto)