Uno de los problemas más comunes en equipos de frontend es que un cambio aparentemente inocente en un componente rompe la UI de otra parte de la aplicación sin que nadie lo note hasta que llega a producción. El snapshot testing es una técnica sencilla y muy efectiva para evitar exactamente eso, y con Vitest se integra de forma natural en proyectos modernos con Vite.
¿Qué es el Snapshot Testing?
La idea es simple: la primera vez que ejecutas un test, Vitest renderiza el componente (o cualquier estructura de datos serializable) y guarda una «foto» de su salida en un archivo .snap. En cada ejecución posterior, compara el resultado actual con esa foto guardada. Si hay diferencias, el test falla y te avisa del cambio.
Esto no reemplaza los tests unitarios o de integración clásicos, pero cubre un ángulo diferente: detectar regresiones visuales o estructurales a nivel de código, sin necesidad de un navegador completo ni herramientas de comparación de imágenes.
Configuración inicial: Vitest en un proyecto Vite
Si ya tienes un proyecto con Vite, añadir Vitest es cuestión de minutos:
npm install -D vitest @testing-library/react jsdom
Luego añade en tu vite.config.ts (o .js):
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
},
})
Con esto, Vitest ya está listo para ejecutar tests en un entorno simulado de DOM.
Tu primer snapshot test
Supón que tienes un componente Badge.tsx que muestra una etiqueta de estado:
// Badge.tsx
export function Badge({ label, type }: { label: string; type: 'success' | 'error' | 'warning' }) {
return (
<span className={`badge badge--${type}`}>{label}</span>
)
}
El test de snapshot sería así:
// Badge.test.tsx
import { render } from '@testing-library/react'
import { expect, test } from 'vitest'
import { Badge } from './Badge'
test('Badge success renderiza correctamente', () => {
const { container } = render(<Badge label="OK" type="success" />)
expect(container).toMatchSnapshot()
})
test('Badge error renderiza correctamente', () => {
const { container } = render(<Badge label="Error" type="error" />)
expect(container).toMatchSnapshot()
})
La primera vez que corres npx vitest, Vitest crea un archivo __snapshots__/Badge.test.tsx.snap con el HTML serializado de cada test. A partir de ahí, cualquier cambio en la estructura del componente hará fallar el test.
Cómo actualizar snapshots deliberadamente
Cuando haces un cambio intencionado en el componente (por ejemplo, añades un atributo aria-label), el test fallará porque el snapshot antiguo ya no coincide. Esto es correcto: el sistema te está pidiendo que confirmes el cambio.
Para actualizar los snapshots después de revisar que el cambio es esperado:
npx vitest --update-snapshots
O en modo interactivo:
npx vitest -u
Una buena práctica es incluir siempre la actualización de snapshots en el pull request junto con el diff del componente, para que el reviewer pueda ver exactamente qué cambió.
Inline Snapshots: snapshots dentro del propio test
Vitest soporta también los inline snapshots, donde la «foto» se guarda directamente en el archivo de test en lugar de en un archivo .snap separado. Resultan útiles cuando los snapshots son pequeños y quieres tenerlo todo en un solo lugar:
test('Badge warning tiene la clase correcta', () => {
const { container } = render(<Badge label="Atención" type="warning" />)
expect(container.firstChild).toMatchInlineSnapshot(`
<span
class="badge badge--warning"
>
Atención
</span>
`)
})
Snapshot testing de datos, no solo de UI
Una ventaja que muchos QAs pasan por alto: los snapshots no son solo para componentes. Puedes usarlos para capturar la salida de funciones de transformación de datos, respuestas de API mockeadas o cualquier objeto JSON complejo:
import { parseApiResponse } from './utils'
test('parseApiResponse transforma los datos correctamente', () => {
const rawData = { id: 1, first_name: 'Ana', last_name: 'García', active: true }
expect(parseApiResponse(rawData)).toMatchSnapshot()
})
Esto es especialmente útil cuando tienes funciones de mapeo o normalización de datos que se usan en muchos sitios: si alguien cambia la lógica, los snapshots lo detectan de inmediato.
Cuándo NO usar snapshot testing
El snapshot testing tiene limitaciones claras que conviene conocer para no abusar de él:
- Componentes con datos dinámicos (timestamps, IDs aleatorios, datos en tiempo real): el snapshot cambiará en cada ejecución. En estos casos, usa
expect.any()o mockea los valores variables. - Componentes muy grandes o complejos: un snapshot de 500 líneas de HTML es difícil de revisar en un PR. Divide los tests en unidades más pequeñas.
- Como sustituto de tests de comportamiento: un snapshot te dice que algo cambió estructuralmente, pero no que la funcionalidad sigue correcta. Combínalo siempre con tests de comportamiento.
Integración en el pipeline de CI
En tu pipeline (GitHub Actions, GitLab CI, etc.), añade el comando de Vitest sin la flag de actualización para que los snapshots fallen si hay diferencias no confirmadas:
# .github/workflows/test.yml
- name: Run tests
run: npx vitest run
Si un desarrollador olvida actualizar los snapshots después de un cambio intencional, el pipeline fallará y lo recordará antes del merge. Ese es exactamente el comportamiento que queremos.
Conclusión
El snapshot testing con Vitest es una herramienta ligera y de bajo coste de mantenimiento para atrapar regresiones en componentes y lógica de transformación de datos. No requiere configuración compleja y se integra perfectamente en flujos de trabajo modernos con Vite y React (o Vue, Svelte, etc.). Úsalo como complemento de tus tests de comportamiento y tendrás una capa extra de seguridad que avisa cuando algo cambia sin que nadie lo haya pedido.





