Pruebas de Integración vs Pruebas End-to-End
Pruebas de integración vs end-to-end para apps web: definiciones claras, herramientas JS y cuándo usar Vitest, MSW, Playwright o Cypress.
Las pruebas de integración verifican que tus propios componentes y módulos funcionan correctamente en conjunto — se ejecutan en CI en milisegundos y simulan los sistemas externos; las pruebas end-to-end controlan toda la aplicación en ejecución a través de la UI real como lo haría un usuario, se ejecutan contra un build desplegado o de staging, y no simulan nada. Esa única distinción resuelve la mayor parte de la confusión, pero oculta un segundo problema específico del desarrollo frontend: la palabra “integración” significa algo diferente para un desarrollador de JavaScript que para un ingeniero de QA de backend, y la línea entre “integración” y “E2E” en el navegador es genuinamente difusa.
Este artículo traza esa línea en términos del stack web. Ofrece definiciones precisas con ejemplos concretos, una comparación según los criterios que realmente determinan cuál escribir, dónde se ubica cada tipo en relación con las pruebas unitarias, las herramientas de JavaScript que corresponden a cada una (Vitest, Jest, Testing Library, MSW, Playwright, Cypress), y una recomendación definitiva sobre cómo distribuir tu presupuesto de pruebas — no la respuesta evasiva de “usa ambas”.
Conclusiones Clave
- Las pruebas de integración renderizan tus propios módulos en conjunto con la red simulada y se ejecutan en CI en milisegundos; las pruebas end-to-end controlan la aplicación desplegada en un navegador real y no simulan nada.
- En una aplicación JavaScript, “integración” generalmente significa Vitest o Jest más Testing Library y MSW; “end-to-end” generalmente significa Playwright o Cypress contra tu URL real.
- La frontera entre integración y E2E en el frontend es un espectro, no una barrera — dónde trazas la línea es una decisión de presupuesto, no una ley.
- Escribe principalmente pruebas de integración para obtener la mejor relación confianza-costo, mantén un conjunto reducido de tres a cinco pruebas E2E para tus flujos de mayor valor, y no intentes cubrir todo con E2E.
- Un bug en producción es, por definición, un flujo que tus pruebas nunca verificaron, lo que convierte la reproducción en sesión de un fallo real en el camino más rápido hacia la prueba que falta.
Pruebas de integración vs pruebas end-to-end de un vistazo
Los dos tipos de prueba difieren en todos los ejes que importan para la planificación del presupuesto: cuánto cubren, qué tan rápido se ejecutan, dónde se ejecutan, qué simulan, con qué frecuencia fallan por razones incorrectas, y qué bugs solo ellas pueden detectar.
| Criterio | Prueba de integración | Prueba end-to-end (E2E) |
|---|---|---|
| Alcance | Varios de tus propios módulos en conjunto (p. ej., un componente + su capa de datos) | Toda la aplicación en ejecución, de principio a fin |
| Velocidad | Milisegundos a pocos segundos | Segundos a minutos por prueba |
| Dónde se ejecuta | Localmente y en CI, en Node o jsdom | Contra un build desplegado o de staging, en un navegador real |
| Qué simula | Sistemas externos — la red, APIs de terceros | Nada (o lo menos posible) |
| Inestabilidad | Baja — sin red real, sin tiempos reales del navegador | Mayor — depende de que todo el sistema permanezca estable |
| Costo / mantenimiento | Menor para escribir y mantener en verde | Mayor; los conjuntos se deterioran a medida que la aplicación cambia |
| Detecta de forma exclusiva | Contratos de API rotos, respuestas con formato incorrecto, cableado de estado | Autenticación, redirecciones, configuración de entorno, scripts de terceros, estado entre páginas |
Qué es una prueba de integración, con un ejemplo web
Discover how at OpenReplay.com.
Una prueba de integración renderiza un árbol de tus propios componentes en conjunto y verifica su comportamiento combinado con la red simulada, comprobando que las unidades que ya probaste de forma unitaria realmente cooperan entre sí. No inicia un navegador y no accede a un servidor real.
Un ejemplo concreto: un componente LoginForm conectado a un hook de autenticación y una solicitud de perfil. La prueba de integración renderiza el formulario, completa los campos, lo envía y verifica que aparece el estado de bienvenida — pero las llamadas POST /api/login y GET /api/profile son interceptadas y respondidas por un mock. Estás probando el cableado entre el formulario, el hook, la capa de solicitudes y el resultado renderizado. No estás probando tu backend real, tu proveedor de autenticación ni tu CDN.
Esta es la capa donde emergen los bugs de contrato: el componente espera user.name pero el handler devuelve user.fullName, una ruta 401 nunca vuelve a renderizar el banner de error, o un indicador de carga nunca se limpia. Las pruebas de integración detectan los bugs que viven entre tus módulos — contratos de API rotos, respuestas con formato incorrecto, cableado de estado — sin el costo de un despliegue real.
Qué es una prueba end-to-end, con un ejemplo web
Una prueba end-to-end controla tu aplicación desplegada real en un navegador real, exactamente como lo haría un usuario, y verifica lo que el usuario ve — sin simular nada. Ejercita el servidor real, la base de datos real, el flujo de autenticación real, las redirecciones reales y cualquier script de terceros que cargue la página.
El ejemplo canónico es un usuario que inicia sesión y realiza una compra: navegar a la URL en vivo, escribir las credenciales, enviar, llegar al dashboard, agregar un artículo al carrito, completar el pago y confirmar el pedido. Cada capa participa — el bundle del frontend, la red, la API, la base de datos, las cookies de sesión y la integración de pagos.
Las pruebas E2E detectan los bugs que solo existen en un despliegue real: autenticación y redirecciones, configuración de entorno, scripts de terceros, violaciones de content-security-policy y estado entre páginas que sobrevive a una navegación. Ninguno de estos se reproduce cuando la red está simulada, que es exactamente por qué el E2E vale su mayor costo para los flujos que generan ingresos.
Dónde se ubican: la pirámide y el trofeo
Ambos tipos de prueba se ubican por encima de las pruebas unitarias, que verifican una única función o componente de forma aislada. La clásica pirámide de pruebas prescribe muchas pruebas unitarias en la base, menos pruebas de integración en el medio, y muy pocas pruebas E2E lentas en la cima. Es un punto de partida sólido para mantener un conjunto de pruebas rápido.
La contrapropuesta moderna es el trofeo de pruebas, popularizado por Kent C. Dodds, que engorda la capa de integración porque las pruebas de integración ofrecen la mejor relación confianza-costo para las aplicaciones web — cercanas al uso real, pero sin la inestabilidad y el tiempo de ejecución del E2E completo. El razonamiento se apoya en el principio rector de Testing Library de que cuanto más se parecen tus pruebas a la forma en que se usa tu software, más confianza pueden darte. Una prueba de integración que renderiza un árbol de componentes real y verifica el comportamiento visible para el usuario se parece mucho más al uso real que una prueba unitaria de un reducer aislado — y lo hace en milisegundos. Considera el trofeo como una visión moderna ampliamente adoptada para aplicaciones web, no como un reemplazo universal de la pirámide.
La difusa frontera del frontend
En el desarrollo frontend, la frontera entre integración y E2E es un espectro, no una barrera: una prueba con Testing Library que renderiza un subárbol de componentes es “integración”, una prueba con Playwright que carga tu URL real es “E2E”, y dónde trazas la línea es una decisión de presupuesto, no una ley. Esa frontera difusa es la mayor fuente de confusión, porque la misma palabra significa cosas diferentes según el contexto.
La literatura de QA de backend divide las pruebas de integración en estrategias bottom-up, top-down y big-bang — esa taxonomía es real, pero es jerga de integración de servidores, y rara vez ayuda a alguien que trabaja con Vitest y Playwright. Ignórala. Lo que importa para un desarrollador web es el mapeo práctico: “integración” casi siempre significa renderizar un árbol de componentes con la red simulada, y “E2E” casi siempre significa controlar la aplicación desplegada en un navegador real. La zona gris en el medio — por ejemplo, una prueba de Playwright apuntando a un servidor de desarrollo local con MSW interceptando la red — está bien; simplemente decide deliberadamente de qué lado del presupuesto proviene.
La cadena de herramientas JS para cada tipo, con código
En una aplicación JavaScript, una “prueba de integración” generalmente significa renderizar un árbol de componentes y verificar su comportamiento con la red simulada — típicamente Vitest o Jest más Testing Library y MSW — mientras que una “prueba end-to-end” significa controlar la aplicación desplegada en un navegador real con Playwright o Cypress.
Mock Service Worker (MSW) es la pieza clave que hace que las pruebas de integración sean realistas sin necesidad de un backend real. Intercepta las solicitudes en la capa de red en ambos entornos — a través de la Service Worker API en el navegador (setupWorker), y mediante intercepción de solicitudes de bajo nivel en Node (setupServer, que usan tus pruebas de Vitest o Jest y que no necesita ningún archivo de service worker). Dado que la intercepción ocurre en el límite en lugar de parcheando fetch, los mismos handlers alimentan tus pruebas de integración, tu servidor de desarrollo e incluso tus ejecuciones de Playwright; la documentación lo expresa directamente: “Imagina usar los mismos mocks de API durante el desarrollo, las pruebas de integración y end-to-end, y luego en tu Storybook.”
A continuación se muestra la misma funcionalidad de login y obtención de perfil como prueba de integración. Observa la API http + HttpResponse de MSW v2, que reemplazó el patrón res/ctx de v1:
// login.integration.test.tsx — Vitest 4 + @testing-library/react 16 + MSW 2
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { LoginForm } from './LoginForm'
const server = setupServer(
http.post('/api/login', () =>
HttpResponse.json({ token: 'abc' })
),
http.get('/api/profile', () =>
HttpResponse.json({ name: 'Ada' })
)
)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
test('logs in and shows the profile name', async () => {
render(<LoginForm />)
await userEvent.type(screen.getByLabelText(/email/i), 'ada@example.com')
await userEvent.type(screen.getByLabelText(/password/i), 'hunter2')
await userEvent.click(screen.getByRole('button', { name: /sign in/i }))
expect(await screen.findByText(/welcome, ada/i)).toBeInTheDocument()
})
No se inicia ningún navegador, no se ejecuta ningún servidor, y la red está completamente controlada — por lo que esta prueba es rápida y determinista. La misma funcionalidad como prueba E2E se ejecuta contra la aplicación desplegada y no simula nada. Playwright controla Chromium, Firefox y WebKit con una sola API, con espera automática y aserciones orientadas a la web:
// login.e2e.spec.ts — Playwright 1.6x
import { test, expect } from '@playwright/test'
test('user logs in and sees their profile', async ({ page }) => {
await page.goto('/login')
await page.getByLabel('Email').fill('ada@example.com')
await page.getByLabel('Password').fill('hunter2')
await page.getByRole('button', { name: 'Sign in' }).click()
await expect(
page.getByRole('heading', { name: /welcome, ada/i })
).toBeVisible()
})
Las aserciones se ven similares; lo que difiere es todo lo que hay debajo. La especificación de Playwright prueba que el endpoint de autenticación real, la redirección y la cookie de sesión funcionan en un navegador real — y paga ese precio en tiempo de ejecución e inestabilidad. Un límite práctico que vale la pena conocer: dado que los React Server Components asíncronos son recientes, Jest actualmente no los soporta y Next.js recomienda pruebas E2E para los componentes asíncronos — un caso concreto donde la capa de integración aún no puede llegar y el E2E justifica su lugar.
Una regla de decisión por escenario
Elige el tipo de prueba según el bug que intentas prevenir, no por costumbre. La regla:
- Nueva interacción entre módulos o un contrato frontend-backend → prueba de integración. Conectar un componente a un nuevo endpoint, un nuevo reducer a un nuevo selector, un formulario a una capa de validación: renderiza el árbol, simula la red con MSW, verifica el comportamiento.
- Un flujo de usuario crítico a través de todo el stack → una prueba E2E. Login, registro, checkout, el único flujo que, si se rompe, cuesta dinero o confianza.
- Todo lo demás → apóyate en la integración, y no lo cubras con E2E.
La recomendación práctica para un equipo web: escribe principalmente pruebas de integración para obtener la mejor relación confianza-costo, mantén un conjunto reducido de tres a cinco pruebas end-to-end para tus flujos de mayor valor, y no intentes cubrir todo con E2E. Una prueba E2E para un tooltip es una carga de mantenimiento; una prueba de integración para él es un seguro económico.
Por qué los conjuntos E2E se deterioran y cómo mantenerlos estables
Los conjuntos E2E se deterioran porque cada prueba depende de que todo el sistema permanezca estable; la solución es mantenerlos en número reducido, verificar resultados visibles para el usuario en lugar de detalles de implementación, y eliminar los que ya no protegen ingresos. Un ID de prueba renombrado, un despliegue de staging más lento, un widget de terceros inestable o una redirección desplazada pueden poner en rojo un conjunto que pasaba sin ninguna regresión real — y un conjunto que da falsas alarmas termina siendo ignorado. Concretamente:
- prefiere selectores basados en roles y etiquetas en lugar de rutas CSS frágiles;
- confía en la espera automática de Playwright en lugar de pausas fijas;
- aísla los datos de prueba; y
- limita el número de pruebas para que el conjunto se mantenga lo suficientemente rápido como para ser confiable.
Sin embargo, ningún conjunto cubre los flujos que no imaginaste — y un bug en producción es, por definición, un camino que tus pruebas de integración y E2E nunca verificaron. La reproducción en sesión demuestra su valor precisamente aquí: una reproducción de un fallo real muestra el flujo no probado exacto que falló — el estado del DOM, las llamadas de red y los errores de consola en el momento del fallo. Las reproducciones de sesión de flujos de login y checkout frecuentemente revelan modos de fallo que ninguna prueba local ejercitó, como una redirección específica del entorno o un script de terceros bloqueado por CSP. El movimiento experto es convertir esa reproducción en la prueba que falta: un fallo a nivel de flujo se convierte en una nueva especificación de Playwright, un fallo de componente o contrato se convierte en una nueva prueba de integración con Testing Library + MSW, y el mismo bug no puede regresar.
Conclusión
La distribución que se sostiene en producción es principalmente integración, algunas E2E de alto valor, y pruebas unitarias por debajo para la lógica pura — integración porque compra la mayor confianza por segundo de tiempo de ejecución, E2E porque algunos bugs solo existen en un navegador desplegado real. Comienza escribiendo pruebas de integración con Vitest o Jest, Testing Library y MSW para los límites de módulos de tu próxima funcionalidad, reserva Playwright para el puñado de flujos que no puedes permitirte romper, y deja que los fallos reales en producción te indiquen qué prueba olvidaste escribir.
Preguntas Frecuentes
¿Puede una sola herramienta como Playwright o Cypress escribir tanto pruebas de integración como end-to-end?
Sí, pero la distinción proviene de cómo delimitas el alcance de la prueba, no de la herramienta. Playwright y Cypress pueden montar un componente de forma aislada con la red simulada, lo que es efectivamente una prueba de integración, o cargar tu URL desplegada y ejercitar todo el stack, lo que es end-to-end. Existen modos de prueba de componentes en ambos, mientras que Vitest o Jest con Testing Library sigue siendo la ruta de integración más común en el ecosistema JavaScript.
¿Necesito el archivo mockServiceWorker.js para MSW en pruebas de integración con Vitest o Jest?
No. El archivo mockServiceWorker.js solo es necesario para la ruta del navegador usando setupWorker, que depende de la Service Worker API. Las pruebas de integración basadas en Node se ejecutan a través de setupServer, que intercepta las solicitudes mediante extensión de clase de bajo nivel en lugar de un service worker, por lo que no se necesita ningún archivo generado. Solo generas el script del worker cuando simulas solicitudes en un navegador real, como durante el desarrollo local o ejecuciones de pruebas basadas en el navegador.
¿Cuándo debería escribir una prueba end-to-end en lugar de una prueba de integración?
Escribe una prueba end-to-end cuando el bug que estás previniendo solo existe en un despliegue real — autenticación, redirecciones, configuración de entorno, cookies de sesión, scripts de terceros, violaciones de content-security-policy o estado entre páginas. Estos nunca se reproducen cuando la red está simulada. Para todo lo que vive entre tus propios módulos, como contratos de API rotos, respuestas con formato incorrecto o cableado de estado, una prueba de integración es más rápida, más económica y menos inestable. Reserva las pruebas end-to-end para tus flujos de mayor valor que generan ingresos.
¿Por qué mis pruebas end-to-end pasan localmente pero fallan en CI?
Los conjuntos E2E dependen de que todo el sistema permanezca estable, por lo que las diferencias entre los entornos local y de CI los rompen sin ninguna regresión real. Las causas comunes incluyen pausas fijas en lugar de espera automática, selectores CSS frágiles que cambian entre builds, tiempos de despliegue más lentos en CI, datos de prueba compartidos o no aislados, y widgets de terceros inestables. Prefiere selectores basados en roles y etiquetas, confía en la espera automática de Playwright, aísla los datos de prueba por ejecución y mantén el conjunto lo suficientemente pequeño para que sea confiable.