Pruebas de componentes con Vitest: Browser Mode e integración con Playwright

Para ser sincero, la primera vez que probé un componente Canvas me llevé un buen susto.
Miré el informe de pruebas con un «PASS» en verde y subí el código con confianza. Al día siguiente, un compañero abrió la página en un navegador real y el Canvas no se renderizaba en absoluto. ¡Pero las pruebas habían pasado!
Entonces entendí: jsdom es un navegador «falso». Simula la API del DOM, pero no puede probar el renderizado real de Canvas, los estilos CSS calculados ni el ciclo de vida de Web Components. Llevaba seis meses con una configuración de pruebas unitarias que, en realidad, solo cubría la superficie.
Por eso Vitest lanzó Browser Mode en la versión 3.0: ejecuta las pruebas directamente en un navegador real. A diferencia de jsdom, que simula el DOM en Node.js, Browser Mode arranca Chromium/Firefox/Safari, renderiza el componente de verdad e interactúa con la API de Playwright. ¿Pasa la prueba? Entonces de verdad pasa.
Este artículo te guía desde la configuración inicial de Browser Mode hasta pruebas prácticas con componentes React/Vue y umbrales de cobertura en CI. Es la tercera entrega de la serie de guías de Vitest: las dos anteriores cubrieron la configuración de pruebas unitarias y el flujo TDD; aquí completamos el bloque de pruebas de componentes.
¿Por qué necesitas Browser Mode?
Puede que te preguntes: ¿jsdom no basta?
Para componentes puramente lógicos —una calculadora, un validador de formularios— jsdom es más que suficiente. Es un simulador de DOM en Node.js, muy rápido y fácil de configurar. Las pruebas unitarias de mis dos artículos anteriores corrieron en jsdom sin problema: datos reactivos, disparo de eventos, todo bien.
Pero en estos escenarios jsdom se queda corto:
- Dibujo con Canvas: jsdom expone la API de Canvas, pero no pinta nada de verdad. Puedes comprobar cuántas veces se llama a
ctx.fillRect(); comprobar si el dibujo es correcto, no. - Estilos CSS calculados:
getComputedStyle()en jsdom devuelve un objeto vacío. En un navegador real, el ancho depende del contenedor padre, padding y border — jsdom no lo calcula. - Web Components: los
connectedCallbackydisconnectedCallbackde elementos personalizados están simulados, pero el momento en que se disparan no coincide con el navegador real. - Renderizado asíncrono: animation frames, requestIdleCallback, IntersectionObserver — jsdom no los implementa o lo hace a medias.
Te cuento una trampa que me pasó. El año pasado tenía un componente con animación CSS para expandir/colapsar un botón. En jsdom, el evento transitionend nunca se dispara — porque no hay transición real. Mockéé el evento; en el navegador real cambiaron la duración de la animación, la prueba seguía en verde y el componente ya estaba roto.
Browser Mode resuelve esto. Renderiza el componente en un navegador real, arranca Chromium (o Firefox/Safari), monta el componente en la página e interactúa con clics, entradas y esperas. Lo que ocurre en el navegador es lo que pruebas.
Un dato útil: en Vitest 3.0, Browser Mode comparte el contexto de Chromium; el navegador se abre una sola vez y todas las pruebas comparten la misma instancia. Según la documentación oficial, es un 30% más rápido que el E2E tradicional con Playwright. Con 50 componentes no esperas a que el navegador se abra y cierre una y otra vez.
Sobre la pirámide de pruebas en componentes hay debate. La pirámide clásica: muchas unitarias, pocas E2E. Pero el blog oficial de Vue defiende una pirámide invertida — alexop.dev: 70% integración, 20% unitarias, 10% E2E. La razón: el componente ya es una unidad de integración (plantilla, estilos, lógica); con jsdom solo pruebas la lógica. Browser Mode llena ese hueco: más real que jsdom, más ligero que Playwright E2E.
Configuración práctica de Browser Mode
Configurar Browser Mode no es tan complicado, aunque hay algunas trampas que ya pisé.
Instalar dependencias
Primero instala Vitest y el proveedor de Browser Mode. La documentación recomienda Playwright:
npm install -D vitest @vitest/browser-playwright
Playwright instala Chromium, Firefox y WebKit. Si solo quieres Chromium (suele bastar):
npx playwright install chromium
Tarda un poco: el paquete de Chromium ronda los 170 MB. Cuando termine la descarga, ya estás casi listo.
Configuración de vitest.config.ts
El archivo es sencillo, pero hay un detalle:
import { defineConfig } from 'vitest/config'
import { playwright } from '@vitest/browser-playwright'
export default defineConfig({
test: {
browser: {
provider: playwright(),
enabled: true,
instances: [{ browser: 'chromium' }],
},
},
})
instances define en qué navegador corren las pruebas. Para compatibilidad multipágina puedes añadir Firefox y WebKit:
instances: [
{ browser: 'chromium' },
{ browser: 'firefox' },
{ browser: 'webkit' }, // Safari
]
Yo suelo dejar solo Chromium: multipágina es más lento y la mayoría de bugs frontend salen ahí. Los problemas de Safari los cubro con Playwright E2E en rutas críticas.
Modo headless vs modo UI
La elección tiene matices.
- Modo headless: el navegador no abre ventana; las pruebas corren en segundo plano. Ideal para CI, rápido, pero no ves el renderizado.
- Modo UI: se abre una ventana; ves el componente renderizarse, recibir clics e inputs. Muy útil al escribir y depurar pruebas en desarrollo.
En desarrollo uso el modo UI:
npx vitest --browser.ui
Vitest abre un panel: lista de pruebas a la izquierda, navegador a la derecha. Al hacer clic en un archivo, el componente aparece en el navegador y ves ejecutarse el código. ¿Un botón no responde? Depuras directamente en el navegador.
En CI uso headless; añade una línea a la config:
browser: {
provider: playwright(),
enabled: true,
headless: true, // CI fuerza headless
instances: [{ browser: 'chromium' }],
}
Convención de nombres de archivos de prueba
La documentación sugiere .browser.test.ts para distinguirlos de .test.ts normales. Lo probé y tiene ventajas:
- Puedes ejecutar pruebas unitarias en jsdom y pruebas de componentes en Browser Mode por separado.
- Si CI falla sin motivo claro, el nombre del archivo te dice que es una prueba de navegador.
Vitest no obliga a ese nombre; .test.ts también vale. Lo importante es incluir en la config el directorio de Browser Mode o ejecutar todo en Browser Mode. Yo los separo: jsdom para unitarias, Browser Mode para componentes.
Pruebas prácticas de componentes React/Vue
Aquí está el uso central de Browser Mode. Se parece a Testing Library, pero con matices; cuando te acostumbras, fluye bien.
Pruebas de componentes React
Instala el adaptador de React:
npm install -D @vitest/browser-react
Ejemplo: un componente Counter que incrementa al hacer clic:
// Counter.browser.test.ts
import { page } from '@vitest/browser/context'
import { userEvent } from '@vitest/browser/context'
import Counter from './Counter'
test('el botón incrementa el contador', async () => {
// Renderizar el componente en el navegador
await page.mount(<Counter />)
// Localizar el botón
const button = page.getByRole('button', { name: 'Count: 0' })
// Hacer clic
await userEvent.click(button)
// Verificar el cambio de texto
await expect.element(button).toHaveTextContent('Count: 1')
})
Comparado con Testing Library: allí render(), aquí page.mount(); allí screen.getByRole(), aquí page.getByRole(). APIs muy parecidas; page es el contexto de Vitest Browser Mode.
Detalle: await expect.element(button). Es la Web Testing API de Vitest; espera automáticamente cambios de estado. No hace falta await waitFor() manual.
Pruebas de componentes Vue
Similar, con el adaptador de Vue:
npm install -D @vitest/browser-vue
Prueba del Counter en Vue:
// Counter.browser.test.ts
import { page } from '@vitest/browser/context'
import { userEvent } from '@vitest/browser/context'
import Counter from './Counter.vue'
test('el botón incrementa el contador', async () => {
// Renderizar el componente Vue
await page.mount(Counter)
// Buscar el botón, hacer clic y verificar
const button = page.getByRole('button', { name: 'Count: 0' })
await userEvent.click(button)
await expect.element(button).toHaveTextContent('Count: 1')
})
Vue 2 usa @vue/test-utils; Browser Mode no lo soporta directamente. En Vue 3, @vitest/browser-vue basta.
Un caso real
En un proyecto tenía un componente de ordenación por arrastre con la API Drag & Drop de HTML5. En jsdom no simulas bien dragstart y drop — solo mocks que «el evento se disparó», sin saber si la lógica de ordenación es correcta.
Con Browser Mode:
test('ordenación por arrastre', async () => {
await page.mount(<SortableList items={['A', 'B', 'C']} />)
const itemA = page.getByText('A')
const itemC = page.getByText('C')
// Arrastrar A detrás de C
await userEvent.dragTo(itemA, itemC)
// Verificar el nuevo orden
const items = page.getByRole('listitem')
await expect.element(items.nth(2)).toHaveTextContent('A')
})
En el navegador real se dispara Drag & Drop, se ejecuta la lógica y cambia el orden. ¿Pasa? Entonces de verdad funciona.
Ese es el valor de Browser Mode: prueba comportamiento real, no simulado.
Guía: Playwright vs Browser Mode
Puede confundir: ¿no son ambos pruebas en navegador? La diferencia:
Browser Mode prueba componentes; Playwright prueba flujos.
Diferencias clave
| Característica | Browser Mode | Playwright |
|---|---|---|
| Alcance | Prueba aislada de un componente | Flujos multipágina |
| Velocidad | ~200 ms/prueba | 2-5 s/prueba |
| Costo de arranque | Instancia de navegador compartida | Arranque independiente por prueba |
| Complejidad | Baja, integrado en Vitest | Alta, proyecto aparte |
| Uso típico | Iteración rápida en desarrollo | Validación de rutas críticas antes del release |
Browser Mode monta un componente en el navegador y prueba solo ese componente. Playwright abre la aplicación completa, navega, inicia sesión, envía formularios — prueba el flujo entero.
Metáfora: Browser Mode es el microscopio del cirujano sobre una célula; Playwright es el médico de chequeo sobre el sistema completo. Cada uno tiene su sitio.
Estrategia combinada
En mis proyectos:
- Browser Mode: todos los componentes UI — botones, formularios, tarjetas, modales. Se escriben en desarrollo y van con el commit. Rápido, feedback inmediato.
- Playwright E2E: 3-5 rutas críticas — login y home, búsqueda y resultados, envío y pedido. Antes del release o en builds diarios de CI.
Ventajas:
- Browser Mode cubre la mayoría de bugs de UI en desarrollo.
- Playwright cubre bugs de integración entre páginas antes del release.
- Costo de mantenimiento controlado: ~50 pruebas de componentes, ~5 E2E; CI no se arrastra.
¿Cuándo usar cuál?
Regla simple:
- Browser Mode: comportamiento UI de un solo componente — clic, input, renderizado, estilos. Cambias el componente y quieres feedback al instante.
- Playwright: flujos multipágina — login → navegación → acción → verificación. O integración frontend + API + base de datos.
Ejemplo: selector de fechas (rango, fechas deshabilitadas, formato) → Browser Mode. Reservar fecha, enviar pedido e ir a pago → Playwright.
Browser Mode solo prueba frontend; Playwright puede cubrir full stack. Con API backend, Playwright E2E prueba frontend + backend; Browser Mode solo el componente, con mocks en backend.
Umbrales de cobertura en CI
La cobertura es el último eslabón en CI. Yo tampoco le daba importancia hasta un refactor que bajó la cobertura del 80% al 60% y un bug en producción me recordó por qué importan los umbrales.
Configuración de cobertura
En vitest.config.ts:
test: {
coverage: {
provider: 'v8', // o 'istanbul'
reporter: ['text', 'json', 'html'],
thresholds: {
lines: 80,
functions: 80,
branches: 75,
statements: 80
}
}
}
¿Qué umbral usar? Mi experiencia:
- Proyecto nuevo: empieza en 50% y sube poco a poco. Umbral alto al inicio presiona de más.
- Proyecto maduro: 80% es razonable; módulos críticos, 90% o 95%.
- No persigas 100%: ramas límite y excepciones a veces no se pueden cubrir; forzar pruebas ahí no compensa.
Ejecutar con cobertura:
npx vitest run --coverage
¿Por debajo del umbral? Vitest falla y CI se rompe. Ese es el gate: código bajo umbral no entra en main.
Integración con GitHub Actions
En el workflow de CI: pruebas + informe de cobertura.
# .github/workflows/test.yml
name: Test
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx playwright install chromium --with-deps
- name: Run tests with coverage
run: npx vitest run --coverage
- name: Report coverage
uses: davelosert/vitest-coverage-report-action@v2
with:
json-summary-path: './coverage/coverage-summary.json'
La action comenta en el PR:
- Cambio porcentual (p. ej. de 80% a 79%, muestra -1%)
- Archivos con cobertura a la baja
- Código nuevo sin cubrir
El autor del PR ve de un vistazo: «Mi código nuevo no tiene cobertura suficiente, hay que añadir pruebas.»
Notas para CI con Browser Mode
Varias trampas:
- Instalación de Playwright: usa
--with-depso Chromium no arranca. - Modo headless:
headless: trueen la config; CI no tiene pantalla. - Timeout: Browser Mode es más lento que jsdom; yo uso 30 segundos.
- Paralelización: instancia compartida; no subas demasiado
maxWorkers; yo limito a 4.
Fragmento de mi workflow:
- name: Run browser tests
run: npx vitest run --coverage --browser.headless
env:
CI: true
--browser.headless evita abrir ventana; CI: true hace que Vitest ajuste comportamiento (p. ej. sin colores en la salida).
Resumen
La idea central: jsdom no prueba comportamiento de navegador real; Browser Mode sí. Canvas, estilos calculados, Web Components, arrastre, animaciones — Browser Mode es la opción.
La configuración es sencilla: instala @vitest/browser-playwright, ajusta unas líneas en vitest.config.ts y listo. Las APIs para React y Vue se parecen a Testing Library; la curva es corta.
Browser Mode no lo arregla todo. Componentes con Browser Mode; flujos con Playwright. En desarrollo cubre componentes UI; antes del release, Playwright E2E en rutas críticas. Cobertura amplia y CI ágil.
Los umbrales de cobertura cierran el círculo: PR por debajo del umbral no se fusiona. Añade coverage-report-action en GitHub Actions y el comentario del PR muestra el cambio al instante.
Si aún no probaste Browser Mode, empieza con un componente simple — un botón, un input — valida la config y luego sube la complejidad. Habrá trampas; cuando las superes y las pruebas fluyan, la productividad sube de verdad.
Configurar Vitest Browser Mode para pruebas de componentes
Configura Browser Mode desde cero, ejecuta pruebas de componentes React/Vue e integra umbrales de cobertura en CI
⏱️ Estimated time: 20 min
- 1
Step 1: Instalar el proveedor de Playwright
Ejecuta npm install -D vitest @vitest/browser-playwright para instalar las dependencias y npx playwright install chromium para descargar el navegador. - 2
Step 2: Configurar vitest.config.ts
En test.browser define provider: playwright(), enabled: true e instances: [{ browser: 'chromium' }]; en CI añade headless: true. - 3
Step 3: Escribir pruebas de componentes
Usa page.mount() para renderizar el componente, page.getByRole() para consultar elementos, userEvent.click() para simular interacciones y expect.element() para las aserciones. - 4
Step 4: Configurar umbrales de cobertura
Define umbrales en coverage.thresholds de vitest.config.ts (lines: 80) e integra vitest-coverage-report-action en GitHub Actions para mostrar los cambios de cobertura en el PR.
FAQ
¿Qué diferencia hay entre Browser Mode y jsdom?
¿Cómo elegir entre Browser Mode y Playwright E2E?
¿Qué umbral de cobertura conviene usar?
¿Browser Mode soporta Vue 2?
¿Qué hay que tener en cuenta con Browser Mode en CI?
11 min de lectura · Publicado el: 17 may 2026 · Actualizado el: 21 ago 2026
Guía de pruebas con Vitest
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Pruebas unitarias con Vitest: flujo TDD y configuración de cobertura
Tutorial práctico de TDD con Vitest: demuestra el ciclo completo Red-Green-Refactor, configura informes de cobertura y umbrales de bloqueo en CI, y explica vi.fn, vi.spy y vi.mock. El framework de pruebas frontend preferido en 2026
Parte 2 de 3
Siguiente
Este es el artículo más reciente de la serie por ahora.



Comentarios
Inicia sesión con GitHub para dejar un comentario