Back

Mocking von API-Aufrufen in Vue-Tests mit Vitest

Mocking von API-Aufrufen in Vue-Tests mit Vitest

Vue-Komponenten existieren selten isoliert. Die meisten von ihnen laden Daten, senden Formulare oder fragen Endpunkte ab. Wenn Sie diese Komponenten ohne Kontrolle über das Netzwerk testen, werden Ihre Tests langsam, instabil und abhängig von externen Diensten. Das Mocking von API-Aufrufen in Vue-Tests hilft, diese Probleme zu beseitigen, indem Sie die Kontrolle über die Antworten erhalten, die Ihre Komponenten empfangen.

Dieser Artikel behandelt zwei praktische Strategien für Vitest Vue API-Mocking: das direkte Ersetzen des API-Moduls mit vi.mock und das Abfangen von Anfragen auf HTTP-Ebene mit Mock Service Worker (MSW). Sie lernen außerdem die Vue-spezifische asynchrone Behandlung kennen, die beide Ansätze zuverlässig funktionieren lässt.

Wichtigste Erkenntnisse

  • Echte Netzwerkaufrufe in Tests erzeugen instabile, langsame, nicht-deterministische Ergebnisse — Mocking gibt Ihnen die volle Kontrolle über Antworten.
  • vi.mock ersetzt das API-Modul auf Import-Ebene und eignet sich ideal für schnelle, isolierte Unit-Tests.
  • MSW fängt Anfragen auf Netzwerkebene ab und lässt Ihre tatsächliche fetch- oder axios-Logik für realistischere Integrationstests ausführen.
  • flushPromises() aus @vue/test-utils wird häufig benötigt, wenn Komponenten auf asynchrone Anfragen warten, bevor sie das DOM aktualisieren.

Warum Sie API-Aufrufe in Vue-Komponententests mit Vitest mocken müssen

Echte Netzwerkaufrufe in Tests erzeugen nicht-deterministische Ergebnisse. Derselbe Test kann lokal bestehen und in der CI fehlschlagen, einfach weil eine externe API langsam oder nicht verfügbar ist. Mocking gibt Ihnen die Kontrolle darüber, was das Netzwerk zurückgibt, sodass Ihre Tests schnell und vorhersehbar bleiben.

Strategie 1: Mocking des API-Moduls mit vi.mock

Der direkteste Ansatz besteht darin, das für HTTP-Aufrufe verantwortliche Modul zu ersetzen, bevor Ihre Komponente es überhaupt sieht.

// 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 wird automatisch über die Imports gehoben
vi.mock('./quote.service')

test('displays a quote after mounting', async () => {
  vi.mocked(fetchQuote).mockResolvedValue({
    id: 1,
    quote: 'Test quote',
    author: 'Tester',
  })

  const wrapper = mount(QuoteCard)

  // Warten, bis alle ausstehenden Promises und DOM-Updates abgeschlossen sind
  await flushPromises()

  expect(wrapper.text()).toContain('Test quote')
})

Ein kritisches Detail: vi.mock wird von Vitest an den Anfang der Datei gehoben und läuft daher vor allen Imports. Dies ist ein beabsichtigtes Verhalten, das in der Vitest Mocking API dokumentiert ist. Das bedeutet, dass Sie die echte Funktion sicher importieren können und trotzdem die gemockte Version in Ihrem Test erhalten.

Der Aufruf von flushPromises() ist bei diesem Muster oft erforderlich. Vue-Komponenten, die Daten beim Mounten laden, aktualisieren ihr DOM asynchron. Hilfsfunktionen wie flushPromises stellen sicher, dass ausstehende Promises aufgelöst werden, bevor Assertions ausgeführt werden, und verhindern so, dass Tests gegen Ladezustände prüfen.

Wann dieser Ansatz verwendet werden sollte: Beim Unit-Testing einer bestimmten Komponente oder eines Dienstes in Isolation, bei dem Sie eine präzise Kontrolle über Rückgabewerte und Aufrufzähler wünschen.

Strategie 2: MSW mit Vitest Vue-Tests für HTTP-Layer-Mocking

Mock Service Worker fängt Anfragen auf Netzwerkebene ab, anstatt JavaScript-Module zu ersetzen. Dies macht Ihre Tests realistischer, da der tatsächliche fetch- oder axios-Aufruf weiterhin ausgeführt wird — MSW fängt ihn nur ab, bevor er den Prozess verlässt.

Installieren Sie MSW und richten Sie es für eine Node-Umgebung ein (Standard für 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: 'MSW quote', 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('displays a quote fetched via MSW', async () => {
  const wrapper = mount(QuoteCard)
  await flushPromises()
  expect(wrapper.text()).toContain('MSW quote')
})

flushPromises() kann auch hier aus demselben Grund erforderlich sein — Vues Reaktivitätssystem benötigt einen Tick, um DOM-Updates anzuwenden, nachdem das Promise aufgelöst wurde.

Wann dieser Ansatz verwendet werden sollte: Bei Integrationstests, bei denen Sie überprüfen möchten, dass Ihre Komponente mit realistischem HTTP-Verhalten funktioniert, oder wenn mehrere Komponenten denselben Endpunkt verwenden. Die eigene Dokumentation von Vitest empfiehlt MSW für Request-Level-Mocking in Node-basierten Testumgebungen.

Wahl zwischen vi.mock und MSW

vi.mockMSW
Abfangen aufModulebeneNetzwerkebene
Testet echte fetch-Logik
Kontrolle pro Test✅ Einfach✅ Via server.use()
Am besten geeignet fürUnit-TestsIntegrationstests

Fazit

Beide Strategien sind gültige Werkzeuge zum Mocking von API-Aufrufen in Vue-Tests. Verwenden Sie vi.mock, wenn Sie schnelle, isolierte Unit-Tests mit präziser Kontrolle über Rückgabewerte wünschen. Verwenden Sie MSW, wenn Ihre Tests den vollständigen Request-Pfad durchlaufen sollen. In beiden Fällen helfen Hilfsfunktionen wie flushPromises() sicherzustellen, dass Vue das Rendern asynchroner Updates abschließt, bevor Assertions ausgeführt werden.

FAQs

Ja. Viele Teams verwenden vi.mock für fokussierte Unit-Tests einzelner Komponenten und MSW für umfassendere Integrationstests, die mehrere Komponenten oder Dienste umfassen. Halten Sie sie in separaten Testdateien, um Verwirrung darüber zu vermeiden, welche Ebene das Mock behandelt.

Vue rendert möglicherweise noch den Ladezustand, wenn Ihre Assertion ausgeführt wird. Wenn Ihre Assertion zufällig mit dem Ladetext übereinstimmt, besteht der Test durch Zufall. Die Konsolenwarnung signalisiert typischerweise nicht behandelte Promise-Rejections oder fehlende awaits. Der Aufruf von flushPromises nach dem Mounten von Komponenten, die asynchrone Anfragen auslösen, hilft, diese Timing-Probleme zu vermeiden.

MSW fängt auf Netzwerkebene ab, sodass es mit jedem HTTP-Client funktioniert, einschließlich axios, fetch und darauf aufbauenden Bibliotheken wie ky oder got. Ihre Wahl des HTTP-Clients beeinflusst nicht, wie MSW das Abfangen handhabt.

Mit vi.mock rufen Sie mockRejectedValue auf, um einen geworfenen asynchronen Fehler zu simulieren. Mit MSW verwenden Sie server.use innerhalb eines bestimmten Tests, um einen Handler zu registrieren, der HttpResponse.json mit einem 4xx- oder 5xx-Statuscode zurückgibt. Beide Ansätze ermöglichen es Ihnen, die Fehlerbehandlung Ihrer Komponente zu überprüfen.

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