Pruebas unitarias con Vitest: de la configuración al flujo TDD

¿Cuánto tardas en configurar Jest en un proyecto ESM? ts-jest, babel-jest, jest.config.js… y problemas de resolución de módulos. Yo pasé una tarde entera para que Jest reconociera imports de archivos .vue.
Vitest necesita una línea de configuración.
No es exageración. La primera vez que ejecuté vitest en un proyecto Vite, los tests terminaron en segundos —como cambiar un PC lento de tres años por uno nuevo.
¿Qué tan rápido es Vitest? Datos oficiales y de la comunidad: arranque en frío ~200 ms (Jest 2-4 s), 500 tests en ~8 s (Jest ~45 s). Comparte config con Vite, TypeScript nativo, API casi igual a Jest —migración en media hora.
Este artículo va de configuración cero al flujo TDD completo, mocking y cobertura. Tanto si empiezas con Vitest como si migras desde Jest, aquí tienes lo que necesitas.
¿Qué es Vitest y por qué es tan rápido?
En pocas palabras, Vitest es el framework de pruebas nativo de Vite.
Si ya usas Vite, Vitest es casi «plug and play». Hereda alias, variables de entorno y CSS —sin transform, moduleFileExtensions ni moduleNameMapper de Jest.
Tres ventajas clave:
Velocidad. Arranque ~200 ms vs 2-4 s de Jest. En proyectos grandes: 500 tests ~8 s vs ~45 s (DEV Community 2026). La diferencia es enorme.
Compatibilidad con Jest. describe, it, expect, vi.fn() —misma sintaxis. Migrar suele ser cambiar imports.
Watch inteligente. «HMR for tests»: al cambiar código, solo reejecuta tests relacionados. Feedback instantáneo en desarrollo.
Instalación y configuración
Requisitos: Vite >= 6.0.0, Node >= 20.0.0.
Instalación
Un comando:
npm install -D vitest
Sin @types/jest, ts-jest, jest-environment-jsdom… TypeScript nativo.
Archivo de configuración
En vite.config.ts o un vitest.config.ts aparte.
Proyecto simple —vite.config.ts:
// vite.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true, // describe, it, expect globales
environment: 'node', // o 'jsdom' para navegador
include: ['tests/**/*.test.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
},
},
})
globals: true evita importar en cada archivo, como en Jest.
Para DOM (componentes), environment: 'jsdom' e instala jsdom:
npm install -D jsdom
Scripts
En package.json:
{
"scripts": {
"test": "vitest",
"test:run": "vitest run"
}
}
npm test = watch; npm run test:run = una ejecución (CI).
Mucho más simple que preset, transform y moduleFileExtensions de Jest.
Escribir pruebas unitarias
Nombres .test.ts o .spec.ts en tests/ o junto al código fuente.
Estructura básica
import { describe, it, expect } from 'vitest'
import { add, divide } from './math'
describe('Math utilities', () => {
it('should add two numbers', () => {
expect(add(2, 3)).toBe(5)
})
it('should throw on division by zero', () => {
expect(() => divide(10, 0)).toThrow('Division by zero')
})
})
Con globals: true, puedes omitir el import.
Aserciones habituales
expect(value).toBe(5)
expect(obj).toEqual({ a: 1 })
expect(value).toBeTruthy()
expect(value).toBeFalsy()
expect(value).toBeNull()
expect(() => fn()).toThrow()
expect(() => fn()).toThrow('Error message')
expect(n).toBeGreaterThan(10)
expect(n).toBeLessThanOrEqual(5)
expect(arr).toContain('item')
expect(str).toMatch(/pattern/)
Filtrar tests
Solo uno: it.only(...). Saltar: it.skip(...). También en describe.
En watch, ver verde o rojo al instante —mucho mejor que esperar segundos en cada arranque de Jest.
Flujo TDD en la práctica
TDD: prueba primero, código después.
Suena al revés, pero obliga a definir qué debe hacer la función antes de implementarla —no tests de relleno tras el código.
Ejemplo: validateEmail.
Paso 1: prueba sin implementación
// tests/validateEmail.test.ts
import { describe, it, expect } from 'vitest'
import { validateEmail } from '../src/validateEmail'
describe('validateEmail', () => {
it('should return true for valid email', () => {
expect(validateEmail('[email protected]')).toBe(true)
})
it('should return false for invalid email', () => {
expect(validateEmail('invalid')).toBe(false)
})
})
La función no existe; la prueba debe fallar. Eso es el primer paso TDD.
Paso 2: mínimo código
// src/validateEmail.ts
export function validateEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}
npm test —ambas en verde.
Paso 3: más bordes
it('should return false for empty string', () => {
expect(validateEmail('')).toBe(false)
})
it('should return false for email without domain', () => {
expect(validateEmail('user@')).toBe(false)
})
it('should return false for email with spaces', () => {
expect(validateEmail('test @example.com')).toBe(false)
})
Paso 4: refactor
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
export function validateEmail(email: string): boolean {
if (!email || email.trim() === '') {
return false
}
return EMAIL_REGEX.test(email)
}
Tests siguen en verde; refactor con red de seguridad.
¿Por qué funciona TDD?
- Diseño antes de código
- Iteración rápida con watch
- Refactor seguro
Empieza con utilidades simples; luego lógica más compleja.
Técnicas avanzadas de Mocking
A menudo hay que simular APIs, BD o librerías. La API de Vitest es similar a Jest; el objeto central es vi.
vi.fn()
import { vi, describe, it, expect } from 'vitest'
describe('vi.fn() demo', () => {
it('tracks calls', () => {
const mockFn = vi.fn()
mockFn('hello')
mockFn('world')
expect(mockFn).toHaveBeenCalledTimes(2)
expect(mockFn).toHaveBeenNthCalledWith(1, 'hello')
})
})
Valores de retorno:
const mockFn = vi.fn().mockReturnValue('mocked result')
const asyncMock = vi.fn().mockResolvedValue({ data: 'ok' })
vi.mock()
import { vi, describe, it, expect, beforeEach } from 'vitest'
import { fetchUser } from './api'
import { UserService } from './UserService'
vi.mock('./api', () => ({
fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Alice' })
}))
describe('UserService', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should fetch user', async () => {
const service = new UserService()
const user = await service.getUser(1)
expect(user.name).toBe('Alice')
expect(fetchUser).toHaveBeenCalledWith(1)
})
})
vi.mock() va arriba del archivo.
vi.spyOn()
import { vi, describe, it, expect, afterEach } from 'vitest'
import { calculator } from './calculator'
describe('spyOn demo', () => {
afterEach(() => {
vi.restoreAllMocks()
})
it('tracks add calls', () => {
const addSpy = vi.spyOn(calculator, 'add')
const result = calculator.add(2, 3)
expect(result).toBe(5)
expect(addSpy).toHaveBeenCalledWith(2, 3)
})
})
La función real se ejecuta; solo se registra la llamada.
Mock de objetos globales
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ data: 'mocked' })
}))
También window, localStorage, etc.
Empieza con vi.fn(); luego vi.mock(). Limpia mocks entre tests.
Cobertura y buenas prácticas
Vitest soporta v8 (rápido) e istanbul. v8 suele bastar.
Configurar cobertura
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
thresholds: {
lines: 80,
functions: 80,
branches: 70,
},
exclude: ['node_modules/', 'tests/', '**/*.d.ts'],
},
}
vitest run --coverage
Informe en terminal y carpeta coverage/ con HTML.
Umbrales
Si no alcanzas thresholds, Vitest falla —útil en CI. 80% es un buen inicio; 100% suele ser contraproducente.
CI/CD
# .github/workflows/test.yml
- name: Run tests with coverage
run: npm run test:run -- --coverage
Sube lcov a Codecov o Coveralls.
Consejos
- No persigas el 100%
- Prioriza el camino crítico
- Elimina tests obsoletos
- Mantén
vitesten watch al desarrollar
Resumen
Vitest: rápido, simple, agradable de usar.
~200 ms de arranque, 500 tests en ~8 s. Config compartida con Vite. API como Jest.
Si usas Vite, Vitest es la opción natural —olvida los dolores de ESM en Jest.
Si aún tienes Jest, prueba media hora de migración: instala, cambia imports, la mayoría corre directo.
TDD: empieza con funciones simples, prueba primero. Acostumbrarse cambia la forma de trabajar.
Las pruebas no son carga; son seguridad. Configura Vitest bien y escribe con más tranquilidad.
Configuración de Vitest y flujo TDD
Pasos completos para configurar Vitest y practicar desarrollo guiado por pruebas
⏱️ Estimated time: 30 min
- 1
Step 1: Instalar Vitest
Ejecuta el comando de instalación:
```bash
npm install -D vitest
```
Requisitos: Vite >= 6.0.0, Node >= 20.0.0 - 2
Step 2: Configurar vite.config.ts
Añade el campo test en la configuración:
```typescript
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true,
environment: 'node',
include: ['tests/**/*.test.ts'],
},
})
```
globals: true evita importar describe, it y expect en cada archivo. - 3
Step 3: Añadir scripts de ejecución
En package.json:
```json
{
"scripts": {
"test": "vitest",
"test:run": "vitest run"
}
}
```
npm test en modo watch; npm run test:run para una sola ejecución (CI). - 4
Step 4: Escribir la primera prueba
Crea tests/math.test.ts:
```typescript
import { describe, it, expect } from 'vitest'
describe('Math', () => {
it('should add numbers', () => {
expect(1 + 1).toBe(2)
})
})
```
Ejecuta npm test para verificar la configuración. - 5
Step 5: Practicar el flujo TDD
Sigue el desarrollo guiado por pruebas:
• Paso 1: escribe la prueba y define el comportamiento esperado
• Paso 2: implementa el mínimo código para pasar
• Paso 3: añade pruebas de bordes
• Paso 4: refactoriza
El modo watch de Vitest da feedback en segundos. - 6
Step 6: Configurar Coverage
Añade configuración de cobertura:
```typescript
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
thresholds: {
lines: 80,
functions: 80,
},
}
```
Ejecuta vitest run --coverage para generar el informe.
FAQ
¿Qué diferencia hay entre Vitest y Jest?
¿Cómo migrar de Jest a Vitest?
• Desinstala paquetes de Jest e instala vitest
• Migra jest.config.js a vite.config.ts
• Cambia imports de 'jest' a 'vitest'
• Sustituye jest.fn()/jest.mock() por vi.fn()/vi.mock()
En la mayoría de casos, 30 minutos bastan.
¿Qué entornos de prueba soporta Vitest?
¿Cómo hacer mock de peticiones API en Vitest?
• vi.fn(): mock de una función con valor de retorno
• vi.mock(): mock de un módulo completo
• vi.spyOn(): vigila llamadas sin reemplazar la implementación
Tras cada prueba usa vi.clearAllMocks() o vi.restoreAllMocks().
¿Cómo configurar Coverage en Vitest?
¿Cuál es el núcleo del flujo TDD?
• Rojo: prueba que falla
• Verde: código mínimo que pasa
• Refactor: mejora la estructura
Con el modo watch de Vitest, feedback en segundos. Escribir primero la prueba aclara el diseño de la función.
6 min de lectura · Publicado el: 14 abr 2026 · Actualizado el: 21 ago 2026
Guía de pruebas con Vitest
Estás leyendo el primer artículo de esta serie. Continúa con el siguiente o abre el hub para ver toda la ruta.
Anterior
Estás al inicio de esta serie.
Siguiente
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



Comentarios
Inicia sesión con GitHub para dejar un comentario