Configuración de Vitest con Next.js
Configura Vitest como un runner de tests rápido y moderno para tu proyecto de Next.js con App Router.
Receta
Tarjeta de referencia rápida — lista para copiar y pegar.
# Instalar dependencias
npm install -D vitest @vitejs/plugin-react jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
globals: true,
setupFiles: ["./vitest.setup.ts"],
include: ["**/*.test.{ts,tsx}"],
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});// vitest.setup.ts
import "@testing-library/jest-dom/vitest";// scripts de package.json
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage"
}
}Cuándo usarlo: Cuando empiezas un proyecto nuevo de Next.js y quieres un runner de tests rápido y nativo de Vite con un modo watch casi instantáneo basado en HMR.
Ejemplo funcional
// src/components/greeting.tsx
interface GreetingProps {
name: string;
}
export function Greeting({ name }: GreetingProps) {
return <h1>Hello, {name}!</h1>;
}// src/components/greeting.test.tsx
import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import { Greeting } from "./greeting";
describe("Greeting", () => {
it("renders the name", () => {
render(<Greeting name="Alice" />);
expect(screen.getByRole("heading")).toHaveTextContent("Hello, Alice!");
});
it("updates when name prop changes", () => {
const { rerender } = render(<Greeting name="Alice" />);
expect(screen.getByRole("heading")).toHaveTextContent("Hello, Alice!");
rerender(<Greeting name="Bob" />);
expect(screen.getByRole("heading")).toHaveTextContent("Hello, Bob!");
});
});Lo que demuestra:
- Configuración mínima de Vitest con el plugin de React y jsdom
- Archivo de configuración que carga los matchers de jest-dom para Vitest
- Alias de rutas que coincide con
@/de tsconfig - Un test de componente sencillo usando Testing Library
Profundización
Cómo funciona
- Vitest usa el pipeline de transformación de Vite bajo el capó, así que los archivos JSX/TSX se compilan mediante
@vitejs/plugin-reactsin necesidad de una configuración de Babel aparte - La opción
environment: "jsdom"crea un DOM de navegador simulado en Node.js para cada archivo de test globals: truehace quedescribe,ityexpectestén disponibles sin importarlos (siguiendo las convenciones de Jest)- El archivo de configuración se ejecuta antes de cada archivo de test — cargar
@testing-library/jest-dom/vitestañade matchers comotoBeInTheDocument()ytoHaveTextContent() - El modo watch usa el grafo de módulos de Vite para volver a ejecutar solo los tests afectados por los archivos modificados, lo que lo hace mucho más rápido que el modo watch de Jest
Variaciones
Usar happy-dom en lugar de jsdom:
// vitest.config.ts
export default defineConfig({
test: {
environment: "happy-dom", // faster but less complete DOM implementation
},
});Anulación del entorno por archivo:
// @vitest-environment happy-dom
import { describe, it } from "vitest";
// This file uses happy-dom regardless of global configCobertura con el proveedor v8:
npm install -D @vitest/coverage-v8// vitest.config.ts
export default defineConfig({
test: {
coverage: {
provider: "v8",
reporter: ["text", "html", "lcov"],
include: ["src/**/*.{ts,tsx}"],
exclude: ["src/**/*.test.{ts,tsx}", "src/**/*.d.ts"],
},
},
});Alias de rutas del App Router desde tsconfig:
// vitest.config.ts — match tsconfig paths exactly
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [react(), tsconfigPaths()],
test: {
environment: "jsdom",
globals: true,
setupFiles: ["./vitest.setup.ts"],
},
});npm install -D vite-tsconfig-pathsNotas de TypeScript
// If using globals: true, add vitest types to tsconfig
// tsconfig.json
{
"compilerOptions": {
"types": ["vitest/globals", "@testing-library/jest-dom"]
}
}Errores comunes
-
Falta
@vitejs/plugin-react— Sin este plugin, el JSX en los archivos de test no se transforma. Vitest no gestiona JSX automáticamente como Jest con Babel. Solución: Incluye siemprereact()en el array de plugins. -
Los alias de rutas no se resuelven — Si usas importaciones
@/components/..., Vitest no lee las rutas detsconfig.jsonpor defecto. Solución: Definealiasmanualmente envitest.config.tso usavite-tsconfig-paths. -
globals: truepero errores de TypeScript — TypeScript no conoce los globals de Vitest a menos que añadas"vitest/globals"acompilerOptions.types. Solución: Actualizatsconfig.jsoncomo se muestra arriba. -
jsdom vs happy-dom —
happy-domes más rápido pero carece de algunas APIs de DOM (getBoundingClientRect,IntersectionObserver). Si los tests fallan conhappy-dom, cambia ajsdom. -
Código exclusivo del servidor de Next.js — Vitest no puede ejecutar código que use
next/headers,next/cacheu otras APIs de Next.js solo para Node en un entornojsdom. Solución: Simula esos módulos o pruébalos por separado.
Alternativas
| Alternativa | Úsala cuando | No la uses cuando |
|---|---|---|
Jest con next/jest | Ya tienes una configuración de Jest o necesitas el ecosistema más amplio de Jest | Quieres un modo watch más rápido y soporte ESM nativo |
| Playwright Component Testing | Necesitas renderizado en un navegador real para tests de componentes | Quieres tests unitarios rápidos que se ejecuten en Node |
| Bun test runner | Tu proyecto usa Bun como runtime | Necesitas el ecosistema de matchers y plugins de Vitest/Jest |
Preguntas frecuentes
¿Por qué necesito @vitejs/plugin-react en la configuración de Vitest?
- Vitest no transforma JSX por defecto como Jest con Babel.
- Sin el plugin
react(), cualquier JSX en archivos de test o de código fuente fallará al compilar. - Incluye siempre
react()en el arraypluginsdevitest.config.ts.
¿Cuál es la diferencia entre los entornos jsdom y happy-dom?
jsdomes una implementación de DOM de navegador más completa, pero más lenta.happy-domes más rápido pero carece de algunas APIs comogetBoundingClientRecteIntersectionObserver.- Si los tests fallan con
happy-dom, cambia ajsdom.
¿Cómo hago que describe, it y expect estén disponibles globalmente sin importaciones?
Establece globals: true en vitest.config.ts y añade "vitest/globals" al array compilerOptions.types de tu tsconfig.json.
¿Qué hace el archivo vitest.setup.ts?
- Se ejecuta antes de cada archivo de test.
- Importar
@testing-library/jest-dom/vitestañade matchers comotoBeInTheDocument()ytoHaveTextContent().
¿Cómo resuelvo alias de rutas @/ en Vitest?
Define alias manualmente en vitest.config.ts:
alias: {
"@": path.resolve(__dirname, "./src"),
}O instala y usa vite-tsconfig-paths como plugin.
Trampa: ¿Por qué TypeScript se queja de expect y describe aunque los tests se ejecuten bien?
globals: truelos hace disponibles en tiempo de ejecución, pero TypeScript no los conoce.- Debes añadir
"vitest/globals"acompilerOptions.typesentsconfig.json.
¿Cómo configuro la cobertura de código con Vitest?
npm install -D @vitest/coverage-v8Luego añade coverage.provider: "v8" y los reporters que quieras en vitest.config.ts.
¿Puedo anular el entorno de test por archivo?
Sí. Añade un comentario al inicio del archivo de test:
// @vitest-environment happy-domEsto anula la configuración global del entorno solo para ese archivo.
Trampa: ¿Por qué fallan mis tests al importar next/headers o next/cache?
- Son APIs de Next.js solo para Node que no funcionan en un entorno
jsdom. - Debes simular esos módulos en tus tests o probarlos en un entorno separado.
¿Cómo tipar correctamente el archivo vitest.config.ts en TypeScript?
Importa defineConfig desde vitest/config:
import { defineConfig } from "vitest/config";
export default defineConfig({ /* ... */ });Esto te da comprobación de tipos completa y autocompletado para todas las opciones de configuración de Vitest.
¿Cuál es la diferencia entre vitest y vitest run?
vitestinicia el modo watch, volviendo a ejecutar los tests cuando cambian los archivos.vitest runejecuta todos los tests una vez y termina — úsalo en CI.
¿Cómo sabe el modo watch de Vitest qué tests volver a ejecutar?
Usa el grafo de módulos de Vite para rastrear dependencias. Solo se vuelven a ejecutar los tests afectados por los archivos modificados, lo que lo hace mucho más rápido que el modo watch de Jest.
Relacionado
- Configuración de Jest con Next.js — el runner de tests alternativo
- Fundamentos de React Testing Library — cómo consultar y hacer aserciones en los tests
- Simulación en tests — simular módulos y APIs
- Estrategia de testing y buenas prácticas — elegir qué probar