Design wechseln

Vitest Komponententests in der Praxis: Browser Mode und Playwright-Integration

Easton editorial illustration: 组件测试样品, jsdom 模拟舱, Browser 真实渲染舱, CI 覆盖率闸门

Ehrlich gesagt bin ich beim ersten Test einer Canvas-Komponente in eine ziemliche Falle getappt.

Ich starrte auf das grüne „PASS“ im Testreport und reichte den Code voller Zuversicht ein. Am nächsten Tag öffnete ein Kollege die Seite im echten Browser – und das Canvas wurde überhaupt nicht gerendert. Der Test war doch durchgelaufen?!

Später wurde mir klar: jsdom ist nur ein „Schein“-Browser. Es simuliert DOM-APIs, aber echtes Canvas-Rendering, berechnete CSS-Stile, der Lebenszyklus von Web Components – all das lässt sich damit nicht testen. Meine halbjährige Unit-Test-Konfiguration hatte nur an der Oberfläche gekratzt.

Deshalb führt Vitest ab Version 3.0 den Browser Mode ein – Tests laufen direkt in echten Browsern. Anders als jsdom, das das DOM in Node.js simuliert, startet Browser Mode Chromium/Firefox/Safari, rendert Komponenten wirklich und interagiert über die Playwright-API. Test bestanden? Dann wirklich bestanden.

Dieser Artikel führt Sie von der Browser-Mode-Konfiguration über React/Vue-Praxis bis zu CI-Coverage-Gates. Es ist Teil drei der Vitest-Testreihe; die ersten beiden Teile behandelten Unit-Test-Setup und TDD – hier fehlt das Puzzlestück Komponententests.

Warum Browser Mode?

Die Frage liegt nahe: Reicht jsdom nicht?

Für reine Logik-Komponenten – einen Taschenrechner, einen Formularvalidator – reicht jsdom völlig. Es ist ein DOM-Simulator in Node.js, schnell und einfach konfiguriert. In meinen ersten beiden Artikeln liefen alle Unit-Tests in jsdom; reaktive Daten und Event-Trigger – kein Problem.

Bei diesen Szenarien scheitert jsdom jedoch:

  • Canvas-Zeichnung: jsdom kennt die Canvas-API, zeichnet aber nicht wirklich. Wie oft ctx.fillRect() aufgerufen wird, lässt sich prüfen; ob das Bild stimmt? Nein.
  • Berechnete CSS-Stile: getComputedStyle() liefert in jsdom ein leeres Objekt. Im echten Browser hängen Breite, Padding und Border vom Elterncontainer ab – jsdom rechnet das nicht.
  • Web Components: connectedCallback und disconnectedCallback sind simuliert, aber der Lebenszyklus triggert anders als im echten Browser.
  • Asynchrones Rendering: Animationsframes, requestIdleCallback, IntersectionObserver – jsdom implementiert das gar nicht oder nur unvollständig.

Ein Beispiel aus eigener Erfahrung: Ein Projekt nutzte CSS-Animationen für aufklappbare Buttons. In jsdom feuert transitionend nie – es gibt keine echte Transition. Der Test mockte das Event; im echten Browser änderte sich die Animationsdauer, der Test blieb „grün“, die Komponente war kaputt.

Browser Mode löst genau das. Komponenten werden in echten Browsern gerendert; Sie schreiben Tests, Vitest startet Chromium (oder Firefox/Safari), mountet die Komponente und nutzt Playwright zum Klicken, Tippen und Warten. Was im Browser passiert, testen Sie auch.

Eine Zahl lohnt sich: Vitest 3.0 teilt sich in Browser Mode einen Chromium-Kontext – der Browser startet einmal, alle Tests nutzen dieselbe Instanz. Offiziell soll das etwa 30 % schneller sein als klassisches Playwright-E2E. Bei 50 Komponententests kein ständiges Starten und Schließen.

Zum Test-Pyramiden-Thema gibt es bei Komponententests Kontroversen. Klassisch: viele Unit-Tests, wenig E2E. Das Vue-Official-Blog und alexop.dev argumentieren für eine invertierte Pyramide – 70 % Integration, 20 % Unit, 10 % E2E. Begründung: Eine Komponente ist bereits eine Integration aus Template, Stil und Logik; jsdom-Unit-Tests prüfen vor allem Logik, Integrationstests das Gesamtverhalten. Browser Mode füllt die Lücke – realer als jsdom, leichter als Playwright-E2E.

Browser Mode konfigurieren – Praxis

Die Konfiguration ist nicht kompliziert, aber ein paar Fallstricke kenne ich aus eigener Erfahrung.

Abhängigkeiten installieren

Installieren Sie Vitest und den Browser-Mode-Provider. Offiziell wird Playwright empfohlen:

npm install -D vitest @vitest/browser-playwright

Playwright bringt Chromium, Firefox und WebKit mit. Für die meisten Fälle reicht Chromium:

npx playwright install chromium

Das dauert – das Chromium-Paket hat etwa 170 MB. Danach sind Sie fast fertig.

vitest.config.ts

Die Konfiguration ist schlank, ein Detail ist wichtig:

import { defineConfig } from 'vitest/config'
import { playwright } from '@vitest/browser-playwright'

export default defineConfig({
  test: {
    browser: {
      provider: playwright(),
      enabled: true,
      instances: [{ browser: 'chromium' }],
    },
  },
})

instances legt fest, in welchem Browser getestet wird. Für Multi-Browser-Kompatibilität können Sie Firefox und WebKit ergänzen:

instances: [
  { browser: 'chromium' },
  { browser: 'firefox' },
  { browser: 'webkit' },  // Safari
]

Ich nutze meist nur Chromium – Multi-Browser ist langsam, die meisten Frontend-Bugs zeigen sich in Chromium. Safari-Probleme decke ich mit Playwright-E2E auf kritischen Pfaden ab.

Headless vs. UI-Modus

Die Wahl ist nicht trivial.

  • Headless: Kein sichtbares Fenster, Tests laufen im Hintergrund. Ideal für CI, schnell, aber ohne visuelles Rendering.
  • UI-Modus: Browserfenster offen – Sie sehen Mount, Klick und Eingabe. Praktisch beim Debuggen in der Entwicklung.

In der Entwicklung nutze ich den UI-Modus:

npx vitest --browser.ui

Vitest öffnet ein Test-Dashboard: links die Testliste, rechts der Browser. Klicken Sie eine Testdatei, die Komponente erscheint – Sie sehen die Ausführung. Button reagiert nicht? Direkt im Browser debuggen.

In CI Headless, eine Zeile in der Config:

browser: {
  provider: playwright(),
  enabled: true,
  headless: true,  // CI erzwingt headless
  instances: [{ browser: 'chromium' }],
}

Dateinamen-Konvention

Offiziell empfiehlt sich .browser.test.ts, getrennt von normalem .test.ts. Vorteile:

  • jsdom-Unit-Tests und Browser-Mode-Komponententests laufen getrennt.
  • Bei mysteriösen CI-Fehlern erkennen Sie am Dateinamen sofort Browser-Tests.

Vitest erzwingt das nicht – .test.ts geht auch. Wichtig ist, Browser-Mode-Verzeichnisse in der Config einzubinden oder alles im Browser Mode laufen zu lassen. Ich trenne: jsdom für Units, Browser Mode für Komponenten.

React/Vue-Komponententests in der Praxis

Das ist der Kern von Browser Mode. Die Syntax weicht etwas von Testing Library ab, gewöhnt man sich aber schnell.

React-Komponententests

React-Adapter installieren:

npm install -D @vitest/browser-react

Beispiel: Counter-Komponente, Klick erhöht den Zähler:

// Counter.browser.test.ts
import { page } from '@vitest/browser/context'
import { userEvent } from '@vitest/browser/context'
import Counter from './Counter'

test('Klick auf Button erhöht Zähler', async () => {
  await page.mount(<Counter />)

  const button = page.getByRole('button', { name: 'Count: 0' })

  await userEvent.click(button)

  await expect.element(button).toHaveTextContent('Count: 1')
})

Vergleich mit Testing Library: dort render(), hier page.mount(). Dort screen.getByRole(), hier page.getByRole(). Die APIs ähneln sich; page ist der Browser-Mode-Kontext.

Ein Detail: await expect.element(button). Das ist Vitests Web Testing API – sie wartet automatisch auf Zustandsänderungen. Kein manuelles await waitFor() nötig; kompakter als Testing Library.

Vue-Komponententests

Ähnlich, mit Vue-Adapter:

npm install -D @vitest/browser-vue

Vue-Counter-Test:

// Counter.browser.test.ts
import { page } from '@vitest/browser/context'
import { userEvent } from '@vitest/browser/context'
import Counter from './Counter.vue'

test('Klick auf Button erhöht Zähler', async () => {
  await page.mount(Counter)

  const button = page.getByRole('button', { name: 'Count: 0' })
  await userEvent.click(button)
  await expect.element(button).toHaveTextContent('Count: 1')
})

Vue 2 mit @vue/test-utils – Browser Mode unterstützt das nicht direkt. Für Vue 3 reicht @vitest/browser-vue.

Praxisbeispiel

In einem Projekt: Drag-and-Sort-Liste mit HTML5 Drag & Drop. In jsdom lassen sich dragstart und drop nicht realistisch simulieren – nur Mocks, die prüfen, ob Events feuern, nicht ob die Sortierung stimmt.

Mit Browser Mode:

test('Drag-and-Drop-Sortierung', async () => {
  await page.mount(<SortableList items={['A', 'B', 'C']} />)

  const itemA = page.getByText('A')
  const itemC = page.getByText('C')

  await userEvent.dragTo(itemA, itemC)

  const items = page.getByRole('listitem')
  await expect.element(items.nth(2)).toHaveTextContent('A')
})

Im echten Browser feuert Drag & Drop wirklich, die Sortierlogik läuft, die Reihenfolge ändert sich. Test grün? Dann passt es wirklich.

Das ist der Wert von Browser Mode – echtes Verhalten, keine Simulation.

Playwright vs. Browser Mode – Entscheidungshilfe

Verwirrend: Beides sind Browser-Tests. Der Unterschied?

Kurz: Browser Mode testet Komponenten, Playwright testet Abläufe.

Kernunterschiede

EigenschaftBrowser ModePlaywright
TestumfangIsolierte EinzelkomponenteMulti-Page-Flows
Ausführungszeitca. 200 ms/Test2–5 s/Test
StartkostenGeteilte Browser-InstanzOft pro Test neu starten
KonfigurationNiedrig, in Vitest integriertHöher, eigenes Projekt
EinsatzSchnelle Iteration in der EntwicklungKritische Pfade vor Release

Browser Mode mountet eine Komponente isoliert im Browser. Playwright öffnet die ganze App – Navigation, Login, Formular – der gesamte Flow.

Metapher: Browser Mode ist der Chirurg am Mikroskop für eine Zelle; Playwright der Hausarzt für das Organsystem. Beides hat seinen Platz.

Kombinationsstrategie

So setze ich es ein:

  • Browser Mode: Alle UI-Komponenten – Buttons, Formulare, Karten, Modals. Mit dem Code committen, schnelles Feedback.
  • Playwright E2E: 3–5 kritische Pfade – Login und Startseite, Suche und Ergebnisse, Submit und Bestellung. Vor Release oder im täglichen CI-Build.

Vorteile:

  1. Browser Mode fängt die meisten UI-Bugs früh ab.
  2. Playwright fängt Cross-Page-Integration ab.
  3. Wartbar: 50 Komponententests, 5 E2E – CI bleibt tragbar.

Wann was?

  • Browser Mode: Einzelkomponente – Klick, Eingabe, Rendering, Stil. Code ändert sich, Feedback sofort.
  • Playwright: Multi-Page – Login → Navigation → Aktion → Assertion. Oder Full-Stack – Frontend, API, Datenbank.

Beispiel: Datumsauswahl – Bereichsgrenzen, gesperrte Tage, Format → Browser Mode. Buchungsseite, Datum wählen, Bestellung absenden, zur Zahlung → Playwright.

Browser Mode testet nur Frontend; Playwright kann Full-Stack abdecken. Mit Backend-API mocken Sie in Browser Mode das Backend; Playwright-E2E kann Frontend und Backend gemeinsam prüfen.

CI-Coverage-Gates

Coverage ist der letzte Baustein in CI. Lange ignorierte ich das – bis nach einem Refactoring die Coverage von 80 % auf 60 % fiel und ein Bug live ging.

Coverage konfigurieren

In vitest.config.ts Schwellenwerte setzen:

test: {
  coverage: {
    provider: 'v8',  // oder 'istanbul'
    reporter: ['text', 'json', 'html'],
    thresholds: {
      lines: 80,
      functions: 80,
      branches: 75,
      statements: 80
    }
  }
}

Schwellenwerte nach Erfahrung:

  • Neues Projekt: Mit 50 % starten, schrittweise erhöhen. Zu hoch am Anfang erzeugt Druck.
  • Reifes Projekt: 80 % ist sinnvoll. Kernmodule 90 % oder 95 %.
  • Nicht 100 % jagen: Randfälle und Exception-Pfade sind oft nicht testbar – erzwungene Tests kosten Zeit ohne Nutzen.

Coverage ausführen:

npx vitest run --coverage

Unter dem Schwellenwert? Vitest bricht ab, CI schlägt fehl – das Gate verhindert Merge mit zu niedriger Coverage.

GitHub Actions

Zwei Schritte: Tests laufen lassen, Coverage melden.

# .github/workflows/test.yml
name: Test

on: [pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci
      - run: npx playwright install chromium --with-deps

      - name: Run tests with coverage
        run: npx vitest run --coverage

      - name: Report coverage
        uses: davelosert/vitest-coverage-report-action@v2
        with:
          json-summary-path: './coverage/coverage-summary.json'

Die Action kommentiert PRs automatisch:

  • Coverage-Delta (z. B. 80 % → 79 %, −1 %)
  • Dateien mit gesunkener Coverage
  • Neuer Code ohne Tests

Autoren sehen sofort: „Neuer Code braucht noch Tests.“

CI-Hinweise für Browser Mode

  1. Playwright installieren: --with-deps, sonst startet Chromium nicht.
  2. Headless: headless: true – in CI gibt es keinen Monitor.
  3. Timeout: Browser Mode ist langsamer als jsdom – ich setze 30 Sekunden.
  4. Parallelität: Geteilte Instanz – maxWorkers: 4 reicht mir.

Mein CI-Schritt:

- name: Run browser tests
  run: npx vitest run --coverage --browser.headless
  env:
    CI: true

--browser.headless hält Fenster zu; CI: true passt Vitest-Verhalten an (z. B. keine Farbausgabe).

Fazit

Kernbotschaft: jsdom testet kein echtes Browserverhalten – Browser Mode schon. Canvas, berechnete CSS-Stile, Web Components, Drag & Drop, Animationen – dafür ist Browser Mode die richtige Wahl.

Setup ist überschaubar: @vitest/browser-playwright installieren, wenige Zeilen in vitest.config.ts, loslegen. React- und Vue-APIs ähneln Testing Library – niedrige Einstiegshürde.

Browser Mode ersetzt Playwright nicht. Komponenten hier, Flows dort: Browser Mode für UI in der Entwicklung, Playwright-E2E für kritische Pfade vor Release. Breite Abdeckung, schnelles CI.

Coverage-Gates sind der Abschluss: Schwellenwert setzen, PRs unter der Grenze blockieren. vitest-coverage-report-action in GitHub Actions – Coverage-Änderungen im PR-Kommentar, Probleme auf einen Blick.

Wenn Sie Browser Mode noch nicht probiert haben: Starten Sie mit einer einfachen Komponente – Button oder Input. Config verifizieren, dann komplexere Fälle. Fallstricke gibt es, aber danach laufen Tests flüssig – und die Entwicklung wird spürbar effizienter.

Vitest Browser Mode für Komponententests konfigurieren

Browser Mode von Grund auf einrichten, React/Vue-Komponententests ausführen und CI-Coverage-Gates integrieren

⏱️ Estimated time: 20 min

  1. 1

    Step 1: Playwright-Provider installieren

    Führen Sie npm install -D vitest @vitest/browser-playwright aus, um Abhängigkeiten zu installieren, und npx playwright install chromium, um den Browser herunterzuladen.
  2. 2

    Step 2: vitest.config.ts konfigurieren

    Setzen Sie in test.browser provider: playwright(), enabled: true und instances: [{ browser: 'chromium' }]. In CI-Umgebungen headless: true hinzufügen.
  3. 3

    Step 3: Komponententests schreiben

    Nutzen Sie page.mount() zum Rendern, page.getByRole() zum Abfragen von Elementen, userEvent.click() für Interaktionen und expect.element() für Assertions.
  4. 4

    Step 4: Coverage-Gates konfigurieren

    Setzen Sie Schwellenwerte in vitest.config.ts coverage.thresholds (z. B. lines: 80) und integrieren Sie vitest-coverage-report-action in GitHub Actions, um PR-Coverage-Änderungen anzuzeigen.

FAQ

Was ist der Unterschied zwischen Browser Mode und jsdom?
jsdom simuliert das DOM in Node.js und kann Canvas-Rendering, berechnete CSS-Stile oder den Web-Components-Lebenszyklus nicht testen. Browser Mode rendert Komponenten in echten Browsern und testet echtes Verhalten.
Wie wähle ich zwischen Browser Mode und Playwright E2E?
Browser Mode eignet sich für Einzelkomponententests (ca. 200 ms/Test) mit sofortigem Feedback in der Entwicklung. Playwright passt zu Multi-Page-Flows (2–5 s/Test) zur Validierung kritischer Pfade vor dem Release. Beides kombinieren.
Welchen Coverage-Schwellenwert soll ich setzen?
Neue Projekte starten bei 50 %, 80 % ist für reife Projekte sinnvoll. Kernmodule können 90 % haben. 100 % anstreben lohnt sich nicht – manche Randfälle sind praktisch nicht testbar.
Unterstützt Browser Mode Vue 2?
Nein. Vue 2 benötigt @vue/test-utils. Vue-3-Projekte können @vitest/browser-vue direkt nutzen.
Was ist bei Browser Mode in CI zu beachten?
Playwright mit --with-deps installieren, headless: true konfigurieren, Timeout-Schwellen erhöhen (30 Sekunden) und parallele Worker begrenzen (maxWorkers: 4).

9 Min. Lesezeit · Veröffentlicht am: 17. Mai 2026 · Aktualisiert am: 14. Juli 2026

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog