Pruebas Unitarias en Vue 3 con Vitest y Vue Test Utils: Guía Definitiva
Este tutorial completo enseña cómo configurar y escribir pruebas unitarias eficientes en componentes de Vue 3 utilizando Vitest como ejecutor de pruebas y Vue Test Utils para montar e interactuar con los componentes.
🚀 Introducción a las Pruebas Unitarias en Vue 3
Las pruebas unitarias son una parte fundamental en el desarrollo de software moderno. Nos permiten garantizar que nuestros componentes y funciones individuales funcionen correctamente de forma aislada, previniendo regresiones cuando escalamos nuestras aplicaciones. En el ecosistema de Vue 3, la combinación de Vitest y Vue Test Utils se ha convertido en el estándar de la industria, reemplazando a herramientas más antiguas como Jest debido a su velocidad ultrarrápida impulsada por Vite.
🛠️ Configuración del Entorno de Pruebas
Para comenzar a escribir pruebas en nuestro proyecto de Vue 3, necesitamos instalar y configurar las herramientas necesarias. Si creaste tu proyecto usando Vite, la integración de Vitest es extremadamente sencilla.
Instalación de Dependencias
Ejecuta el siguiente comando en tu terminal para instalar Vitest, Vue Test Utils y la librería de entorno de DOM (jsdom):
npm install --save-dev vitest @vue/test-utils jsdom
Configuración de Vite
Modifica tu archivo de configuración vite.config.js (o vite.config.ts) para incluir la sección de pruebas de Vitest:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
environment: 'jsdom',
globals: true
}
})
environment: 'jsdom' es obligatoria ya que necesitamos simular un navegador para que Vue pueda montar los componentes correctamente en memoria.🧩 Creando Nuestro Primer Componente y su Prueba
Vamos a construir un componente simple pero funcional llamado Contador.vue que maneje un estado reactivo y emita eventos.
El Componente: Contador.vue
Crea un archivo en tu carpeta de componentes con el siguiente contenido:
<script setup>
import { ref } from 'vue'
const props = defineProps({
initialValue: {
type: Number,
default: 0
}
})
const emit = defineEmits(['increment'])
const count = ref(props.initialValue)
const increment = () => {
count.value++
emit('increment', count.value)
}
</script>
<template>
<div class="contador">
<span class="count-display">{{ count }}</span>
<button @click="increment">Incrementar</button>
</div>
</template>
La Prueba: Contador.spec.js
Ahora crearemos el archivo de pruebas correspondiente en una carpeta __tests__ o junto al componente:
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import Contador from '../Contador.vue'
describe('Contador.vue', () => {
it('renderiza el valor inicial correctamente', () => {
const wrapper = mount(Contador, {
props: {
initialValue: 5
}
})
const display = wrapper.find('.count-display')
expect(display.text()).toBe('5')
})
it('incrementa el contador y emite un evento al hacer clic en el botón', async () => {
const wrapper = mount(Contador, {
props: {
initialValue: 0
}
})
const button = wrapper.find('button')
await button.trigger('click')
expect(wrapper.find('.count-display').text()).toBe('1')
expect(wrapper.emitted()).toHaveProperty('increment')
expect(wrapper.emitted().increment[0]).toEqual([1])
})
})
🔄 Probando Componentes con Formularios y Entradas de Usuario
Las pruebas se vuelven más interesantes cuando interactuamos con formularios. Creemos un componente de registro rápido llamado FormularioRegistro.vue.
Componente FormularioRegistro.vue
<script setup>
import { ref } from 'vue'
const email = ref('')
const submitted = ref(false)
const emit = defineEmits(['submit'])
const handleSubmit = () => {
if (email.value.includes('@')) {
submitted.value = true
emit('submit', email.value)
}
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<input type="email" v-model="email" data-testid="email-input" />
<button type="submit">Registrarse</button>
<p v-if="submitted" data-testid="success-msg">¡Registro exitoso!</p>
</form>
</template>
Prueba para FormularioRegistro.spec.js
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import FormularioRegistro from '../FormularioRegistro.vue'
describe('FormularioRegistro.vue', () => {
it('muestra mensaje de éxito al enviar un email válido', async () => {
const wrapper = mount(FormularioRegistro)
const input = wrapper.find('[data-testid="email-input"]')
await input.setValue('test@example.com')
const form = wrapper.find('form')
await form.trigger('submit')
const successMsg = wrapper.find('[data-testid="success-msg"]')
expect(successMsg.exists()).toBe(true)
expect(wrapper.emitted().submit[0]).toEqual(['test@example.com'])
})
})
await al usar setValue, trigger o al interactuar con el DOM para asegurar que Vue procese las actualizaciones reactivas antes de hacer las afirmaciones.🌐 Mocking de APIs y Dependencias Externas
En aplicaciones reales, los componentes consumen servicios externos o APIs. Vitest nos permite simular (mockear) estas llamadas fácilmente.
Componente que consume una API: PerfilUsuario.vue
<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
const props = defineProps({ userId: String })
const user = ref(null)
const error = ref(null)
onMounted(async () => {
try {
const response = await axios.get(`/api/users/${props.userId}`)
user.value = response.data
} else {
error.value = 'No se pudo cargar el usuario'
} catch (err) {
error.value = 'Error de red'
}
})
</script>
<template>
<div>
<div v-if="user" data-testid="user-name">{{ user.name }}</div>
<div v-if="error" data-testid="error-msg">{{ error }}</div>
</div>
</template>
Prueba con Mock de Axios
import { mount } from '@vue/test-utils'
import { describe, it, expect, vi } from 'vitest'
import PerfilUsuario from '../PerfilUsuario.vue'
import axios from 'axios'
vi.mock('axios')
describe('PerfilUsuario.vue', () => {
it('muestra los datos del usuario cuando la petición es exitosa', async () => {
axios.get.mockResolvedValueOnce({
data: { name: 'Juan Pérez' }
})
const wrapper = mount(PerfilUsuario, {
props: { userId: '1' }
})
// Esperar a que se resuelvan las promesas pendientes del ciclo de vida
await vi.dynamicImportSettled()
await wrapper.vm.$nextTick()
expect(wrapper.find('[data-testid="user-name"]').text()).toBe('Juan Pérez')
})
})
📊 Buenas Prácticas y Consejos Avanzados
data-testid en lugar de clases CSS o etiquetas HTML genéricas que cambian frecuentemente.🔍 Preguntas Frecuentes (FAQs)
¿Puedo usar Jest en lugar de Vitest con Vue 3? Sí, es posible, pero requiere una configuración adicional con Babel y transformadores que complican el proyecto. Vitest está optimizado de forma nativa para Vite y Vue 3.
¿Cómo ejecuto las pruebas en modo interactivo? Puedes ejecutar npx vitest en tu terminal para iniciar el entorno interactivo con recarga en caliente (HMR).
🎯 Conclusión
Dominar las pruebas unitarias en Vue 3 con Vitest y Vue Test Utils te otorgará la confianza necesaria para mantener bases de código robustas y escalables. La velocidad de Vitest combinada con la potencia de Vue Test Utils hace que el proceso de desarrollo sea ágil y agradable. ¡Es hora de aplicarlo en tus propios proyectos!
Tutoriales relacionados
- Aprovechando la API de Contexto en Vue 3 para la Inyección de Dependencias (Provide/Inject)intermediate18 min
- Construyendo Aplicaciones Multilingües Escalables en Vue 3 con vue-i18n y Lazy Loadingintermediate8 min
- Gestión de Estado Centralizada con Pinia en Vue 3: Guía Completaintermediate18 min
- Optimización de Rendimiento en Vue.js 3: Estrategias Avanzadas para Aplicaciones Rápidasintermediate15 min
- Consumiendo APIs REST en Vue 3 con Axios y Fetch: Guía Completaintermediate20 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!