Pruebas de extremo a extremo
Una prueba de extremo a extremo ejecuta la aplicación real, construida e iniciada de la misma forma en que los usuarios la obtienen, y la maneja desde el exterior: un navegador haciendo clic en páginas, un shell ejecutando una CLI, o un cliente llamando a una API. Detecta lo que las pruebas unitarias no pueden: cableado, configuración, enrutamiento y las uniones entre partes. También es el tipo de prueba más lento y frágil, por lo que cada una debe ganarse su lugar y estar escrita para dar la misma respuesta en cada ejecución.
La habilidad hermana unit-testing cubre las pruebas de unidades individuales. Cuando un comportamiento se puede comprobar en cualquier nivel, compruébalo con una prueba unitaria.
Las reglas del repositorio son lo primero
Utiliza el framework, la configuración, la carpeta, los fixtures y los scripts e2e del repositorio. Lee las pruebas existentes y la configuración (playwright.config.*, cypress.config.*, etc.) antes de escribir una, y adáptate a ellas. AGENTS.md, CONTRIBUTING.md y los scripts del paquete indican cómo se ejecutan las pruebas e2e y contra qué. Si entran en conflicto con esta habilidad, prevalecen. No agregues un segundo framework e2e.
Herramienta por defecto
Cuando el repositorio aún no tenga una herramienta e2e, utiliza Playwright (@playwright/test) para cualquier cosa que involucre un navegador. Funciona para cualquier aplicación web, independientemente del lenguaje en el que esté escrito el backend. Los ejemplos aquí usan Playwright. Las reglas se aplican con cualquier herramienta.
Qué merece una prueba e2e
- Viajes críticos: registro, inicio de sesión, la tarea principal para la que existe el producto, proceso de pago y cualquier cosa que se haya roto en producción antes.
- Puntos de integración: una página que necesita el backend real, la autenticación o el enrutamiento para funcionar.
- Un camino feliz por viaje, además de los fallos que un usuario realmente encontraría (una contraseña incorrecta, un pago rechazado).
Empuja todo lo demás hacia abajo a pruebas unitarias: cada regla de validación, cada caso de formato, cada rama de un cálculo. Un conjunto de pruebas e2e que intenta cubrir todos los casos se vuelve demasiado lento para ejecutar y demasiado inestable para confiar en él.
Localizadores
Encuentra elementos como lo haría un usuario, para que las pruebas sobrevivan a cambios de marcado y estilo.
- Preferir, en orden: rol y nombre accesible (
getByRole('button', { name: 'Save' })), etiqueta (getByLabel('Email')), texto visible, luego un ID de prueba (getByTestId) cuando nada orientado al usuario sea estable. - Evitar: clases CSS, XPath,
nth-childy posiciones de elementos. Se rompen cuando el diseño cambia y no dicen nada sobre lo que el usuario ve. - Si un elemento no se puede encontrar por rol o etiqueta, a menudo es un error de accesibilidad. Dilo en lugar de recurrir a un selector frágil.
Espera
Nunca esperes un tiempo fijo. sleep, waitForTimeout y setTimeout son demasiado cortos en una máquina CI lenta y pierden tiempo en una rápida.
- Utiliza aserciones que se reintentan hasta que pasan o expiran:
await expect(page.getByRole('alert')).toHaveText('Saved'). - Espera la condición que el usuario esperaría: un elemento visible, una URL cambiada, una respuesta recibida (
page.waitForResponse), un archivo escrito. - No aumentes los tiempos de espera para solucionar un fallo. Si algo legítimamente toma mucho tiempo, aumenta el tiempo de espera para ese paso concreto y explica por qué.
- No fuerces acciones más allá de las comprobaciones del framework (
{ force: true }). Si un clic necesita forzarse, algo está cubriendo el elemento o no está listo, y un usuario real se encontraría con el mismo problema.
Datos de prueba y aislamiento
- Cada prueba configura su propio estado y se ejecuta sola, en cualquier orden y en paralelo con las demás. Ninguna prueba depende de que otra se haya ejecutado primero.
- Haz que los datos sean únicos por prueba (un correo electrónico o ID generado), para que los trabajadores paralelos y los reintentos no colisionen.
- Configura el estado a través de la puerta trasera. Crea usuarios y registros a través de una API, un script de semilla o un fixture, no haciendo clic en la interfaz de usuario. Solo el viaje bajo prueba pasa por la interfaz de usuario.
- Inicia sesión una vez y reutilízalo. Guarda el estado autenticado en un paso de configuración (
storageStatede Playwright) en lugar de iniciar sesión a través del formulario en cada prueba. - Falsifica solo lo que no posees. Simula servicios de terceros (pagos, correo electrónico, mapas) en el límite de la red con
page.route()o un simulacro local. Mantén tu propio frontend, backend y base de datos reales, o no es una prueba de extremo a extremo.
Pruebas de extremo a extremo de CLI y API
Las mismas reglas se aplican sin navegador. Ejecuta pruebas de CLI con el ejecutor de pruebas unitarias del repositorio (Vitest para JavaScript y TypeScript), en una configuración o proyecto separado para que no ralenticen el conjunto de pruebas unitarias. Las pruebas de API pueden usar el fixture request de Playwright, o el mismo ejecutor.
- Ejecuta el artefacto construido, de la misma manera que los usuarios lo ejecutan, no el código fuente a través de un punto de entrada solo para pruebas.
- Dale a cada prueba un directorio temporal nuevo como su directorio de trabajo, y apunta
HOME, las rutas de configuración y los cachés hacia él, para que las pruebas no lean ni cambien los archivos reales del desarrollador. - Afirma el código de salida, stdout, stderr y los archivos creados, por separado. Una prueba que solo comprueba "no lanzó una excepción" se pierde la salida incorrecta.
- Elimina o congela lo que cambia entre ejecuciones (marcas de tiempo, rutas temporales, duraciones) antes de comparar la salida.
Cuando una prueba falla
Lee la evidencia antes de cambiar nada.
- Obtén los artefactos. Activa trazas, capturas de pantalla y vídeo para fallos (Playwright:
trace: 'on-first-retry',screenshot: 'only-on-failure'), y abre la traza desde la ejecución de CI (npx playwright show-trace <trace.zip>). La traza muestra la página, la red y la consola en cada paso. - Decide de quién es el error. Si la aplicación está mal, arregla la aplicación; la prueba hizo su trabajo. Si la prueba está mal, arregla la prueba. Si el entorno está mal (un servicio caído, un puerto en uso), arregla la configuración.
- Reprodúcelo con el mismo comando, navegador y configuración que usa CI, incluido el modo sin cabeza y el número de trabajadores.
- Para una prueba inestable, encuentra la condición de carrera. Las causas habituales: una espera fija, actuar antes de que la página esté lista, datos compartidos con otra prueba, o una animación o notificación que cubre el elemento. Corrige esa causa, luego ejecuta la prueba muchas veces seguidas (
npx playwright test <archivo> --repeat-each=20 --retries=0) antes de darla por solucionada.
Los reintentos de CI pueden mantener un pipeline verde, pero una prueba que solo pasó en un reintento sigue fallando. Trata "inestable" en un informe como un error que corregir, no un estado. Nunca omitas, elimines o pongas en cuarentena una prueba para obtener un estado verde sin el acuerdo del usuario, y nunca aflojes una aserción para que pase.
Lista de verificación
Antes de entregar pruebas e2e, o al revisarlas:
- El viaje necesita una prueba e2e. Los casos que una prueba unitaria puede comprobar son pruebas unitarias.
- Los localizadores usan rol, etiqueta, texto o un ID de prueba. Sin cadenas CSS ni XPath.
- No hay esperas fijas, acciones forzadas ni tiempos de espera globales aumentados.
- Cada prueba crea sus propios datos únicos y se ejecuta sola y en paralelo.
- Solo se simulan servicios de terceros.
- Los fallos dejan una traza o captura de pantalla en CI.
- La prueba nueva o corregida pasó muchas veces seguidas con reintentos desactivados.