Back

Simulación de llamadas API en pruebas de Vue con Vitest

Simulación de llamadas API en pruebas de Vue con Vitest

Los componentes de Vue rara vez funcionan de forma aislada. La mayoría de ellos obtienen datos, envían formularios o consultan endpoints. Cuando pruebas esos componentes sin controlar la red, tus pruebas se vuelven lentas, inestables y dependientes de servicios externos. La simulación de llamadas API en pruebas de Vue ayuda a eliminar esos problemas al permitirte controlar las respuestas que reciben tus componentes.

Este artículo cubre dos estrategias prácticas para la simulación de API en Vue con Vitest: reemplazar el módulo API directamente con vi.mock, e interceptar solicitudes en la capa HTTP usando Mock Service Worker (MSW). También aprenderás el manejo asíncrono específico de Vue que hace que ambos enfoques funcionen de manera confiable.

Puntos Clave

  • Las llamadas de red reales en las pruebas producen resultados lentos, inestables y no deterministas — la simulación te da control total sobre las respuestas.
  • vi.mock reemplaza el módulo API a nivel de importación, haciéndolo ideal para pruebas unitarias rápidas y aisladas.
  • MSW intercepta solicitudes en la capa de red, permitiendo que tu lógica real de fetch o axios se ejecute para pruebas de integración más realistas.
  • flushPromises() de @vue/test-utils es comúnmente necesario cuando los componentes esperan solicitudes asíncronas antes de actualizar el DOM.

Por qué debes simular llamadas API en pruebas de componentes Vue con Vitest

Las llamadas de red reales en las pruebas producen resultados no deterministas. La misma prueba puede pasar localmente y fallar en CI simplemente porque una API externa está lenta o no disponible. La simulación te da control sobre lo que devuelve la red, por lo que tus pruebas se mantienen rápidas y predecibles.

Estrategia 1: Simulación del módulo API con vi.mock

El enfoque más directo es reemplazar el módulo responsable de las llamadas HTTP antes de que tu componente lo vea.

// quote.service.ts
export async function fetchQuote() {
  const response = await fetch('https://api.example.com/quotes/random')
  return response.json()
}
// QuoteCard.test.ts
import { mount, flushPromises } from '@vue/test-utils'
import { vi, test, expect } from 'vitest'
import { fetchQuote } from './quote.service'
import QuoteCard from './QuoteCard.vue'

// vi.mock se eleva automáticamente por encima de las importaciones
vi.mock('./quote.service')

test('muestra una cita después de montar', async () => {
  vi.mocked(fetchQuote).mockResolvedValue({
    id: 1,
    quote: 'Cita de prueba',
    author: 'Probador',
  })

  const wrapper = mount(QuoteCard)

  // Espera a que todas las promesas pendientes y actualizaciones del DOM se resuelvan
  await flushPromises()

  expect(wrapper.text()).toContain('Cita de prueba')
})

Un detalle crítico: vi.mock es elevado al inicio del archivo por Vitest, por lo que se ejecuta antes de cualquier importación. Este es un comportamiento intencional documentado en la API de simulación de Vitest. Esto significa que puedes importar de forma segura la función real y aún así recibir la versión simulada dentro de tu prueba.

La llamada a flushPromises() a menudo es requerida en este patrón. Los componentes de Vue que obtienen datos al montarse actualizan su DOM de forma asíncrona. Utilidades como flushPromises aseguran que las promesas pendientes se resuelvan antes de que se ejecuten las aserciones, evitando que las pruebas hagan aserciones contra estados de carga.

Cuándo usar este enfoque: Pruebas unitarias de un componente o servicio específico de forma aislada, donde quieres un control estricto sobre los valores de retorno y el conteo de llamadas.

Estrategia 2: MSW con pruebas de Vue en Vitest para simulación a nivel HTTP

Mock Service Worker intercepta solicitudes a nivel de red en lugar de reemplazar módulos JavaScript. Esto hace que tus pruebas sean más realistas porque la llamada real a fetch o axios aún se ejecuta — MSW simplemente la intercepta antes de que salga del proceso.

Instala MSW y configúralo para un entorno Node (el predeterminado para Vitest):

npm install -D msw
// test/server.ts
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'

export const server = setupServer(
  http.get('https://api.example.com/quotes/random', () => {
    return HttpResponse.json({ id: 1, quote: 'Cita MSW', author: 'MSW' })
  })
)
// QuoteCard.test.ts
import { mount, flushPromises } from '@vue/test-utils'
import { test, expect, beforeAll, afterEach, afterAll } from 'vitest'
import { server } from './test/server'
import QuoteCard from './QuoteCard.vue'

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

test('muestra una cita obtenida vía MSW', async () => {
  const wrapper = mount(QuoteCard)
  await flushPromises()
  expect(wrapper.text()).toContain('Cita MSW')
})

flushPromises() puede seguir siendo necesario aquí por la misma razón — el sistema de reactividad de Vue necesita un tick para aplicar las actualizaciones del DOM después de que la promesa se resuelva.

Cuándo usar este enfoque: Pruebas de estilo de integración donde quieres verificar que tu componente funciona con comportamiento HTTP realista, o cuando múltiples componentes comparten el mismo endpoint. La propia documentación de Vitest recomienda MSW para simulación a nivel de solicitud en entornos de prueba basados en Node.

Elegir entre vi.mock y MSW

vi.mockMSW
Intercepta a nivelMóduloRed
Prueba la lógica real de fetch
Control por prueba✅ Fácil✅ Vía server.use()
Mejor paraPruebas unitariasPruebas de integración

Conclusión

Ambas estrategias son herramientas válidas para simular llamadas API en pruebas de Vue. Usa vi.mock cuando quieras pruebas unitarias rápidas y aisladas con control preciso sobre los valores de retorno. Usa MSW cuando quieras que tus pruebas ejerciten la ruta completa de la solicitud. En cualquier caso, utilidades como flushPromises() ayudan a asegurar que Vue termine de renderizar las actualizaciones asíncronas antes de que se ejecuten las aserciones.

Preguntas Frecuentes

Sí. Muchos equipos usan vi.mock para pruebas unitarias enfocadas de componentes individuales y MSW para pruebas de integración más amplias que abarcan múltiples componentes o servicios. Mantenlos en archivos de prueba separados para evitar confusión sobre qué capa está manejando la simulación.

Vue puede seguir renderizando el estado de carga cuando tu aserción se ejecuta. Si tu aserción coincide con el texto de carga, la prueba pasa por coincidencia. La advertencia de consola típicamente señala rechazos de promesas no manejados o awaits faltantes. Llamar a flushPromises después de montar componentes que disparan solicitudes asíncronas ayuda a evitar estos problemas de sincronización.

MSW intercepta a nivel de red, por lo que funciona con cualquier cliente HTTP incluyendo axios, fetch, y bibliotecas construidas sobre ellos como ky o got. Tu elección de cliente HTTP no afecta cómo MSW maneja la intercepción.

Con vi.mock, llama a mockRejectedValue para simular un error asíncrono lanzado. Con MSW, usa server.use dentro de una prueba específica para registrar un manejador que devuelva HttpResponse.json con un código de estado 4xx o 5xx. Ambos enfoques te permiten verificar el manejo de errores de tu componente.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay