Cambiar tema

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

Easton editorial illustration: 组件测试样品, jsdom 模拟舱, Browser 真实渲染舱, CI 覆盖率闸门

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 connectedCallback y disconnectedCallback de 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ísticaBrowser ModePlaywright
AlcancePrueba aislada de un componenteFlujos multipágina
Velocidad~200 ms/prueba2-5 s/prueba
Costo de arranqueInstancia de navegador compartidaArranque independiente por prueba
ComplejidadBaja, integrado en VitestAlta, proyecto aparte
Uso típicoIteración rápida en desarrolloValidació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:

  1. Browser Mode cubre la mayoría de bugs de UI en desarrollo.
  2. Playwright cubre bugs de integración entre páginas antes del release.
  3. 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:

  1. Instalación de Playwright: usa --with-deps o Chromium no arranca.
  2. Modo headless: headless: true en la config; CI no tiene pantalla.
  3. Timeout: Browser Mode es más lento que jsdom; yo uso 30 segundos.
  4. 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. 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. 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. 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. 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?
jsdom simula el DOM en Node.js y no puede probar el renderizado de Canvas, los estilos CSS calculados ni el ciclo de vida de Web Components. Browser Mode renderiza componentes en un navegador real y prueba el comportamiento real.
¿Cómo elegir entre Browser Mode y Playwright E2E?
Browser Mode encaja en pruebas de un solo componente (~200 ms/prueba) con feedback inmediato en desarrollo. Playwright encaja en flujos multipágina (2-5 s/prueba) para validar rutas críticas antes del release. Lo ideal es combinarlos.
¿Qué umbral de cobertura conviene usar?
En proyectos nuevos, empieza con 50%; en proyectos maduros, 80% es razonable. Los módulos críticos pueden ir a 90%. No persigas el 100%: algunos casos límite no se pueden cubrir.
¿Browser Mode soporta Vue 2?
No. Para Vue 2 usa @vue/test-utils. En proyectos Vue 3 puedes usar @vitest/browser-vue directamente.
¿Qué hay que tener en cuenta con Browser Mode en CI?
Al instalar Playwright usa --with-deps, configura headless: true, sube el timeout (30 segundos) y limita la paralelización (maxWorkers: 4).

11 min de lectura · Publicado el: 17 may 2026 · Actualizado el: 21 ago 2026

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog