Vitest hat sich 2026 als der De-facto-Standard für Unit-Tests in Vite-Projekten etabliert.
Blitzschnelle Ausführung, native ESM-Unterstützung, eine Jest-kompatible API und ein hervorragendes
Developer-Experience machen ihn zur ersten Wahl für moderne TypeScript-Projekte — und mit
Claude Code als KI-Copiloten wird Test-driven Development so flüssig wie nie zuvor.
In diesem Guide zeige ich dir, wie du Vitest von Null zum produktionsreifen Test-Setup bringst:
Setup in vite.config.ts, Mocking mit vi, React Testing Library
Integration, Coverage-Konfiguration und das interaktive Vitest UI. Claude Code übernimmt dabei
die Boilerplate, schlägt Edge Cases vor und debuggt rote Tests eigenständig.
Vitest vs. Jest 2026 — schneller Vergleich
| Feature |
Vitest |
Jest |
| Native ESM |
✓ nativ |
✗ Transform nötig |
| Start-Geschwindigkeit |
✓ <100 ms |
∅ 1–3 s |
| Vite-Config geteilt |
✓ ja |
✗ separate Config |
| In-Source Testing |
✓ ja |
✗ nein |
| Browser Mode |
✓ experimentell |
∅ via Jest-env-jsdom |
| TypeScript |
✓ ohne Config |
∅ ts-jest nötig |
| Watch Mode |
✓ HMR-basiert |
∅ stabiler |
Claude Code Workflow-Tipp
Öffne Claude Code im Projektverzeichnis und schreibe: "Erstelle mir ein vollständiges Vitest-Setup mit Coverage und React Testing Library." Claude analysiert deine package.json, passt die vite.config.ts an und generiert Beispiel-Tests — komplett ohne Copy-Paste.
01 · Setup
Vitest Setup: vite.config.ts
Vitest ist nahtlos in das Vite-Ökosystem integriert. Die Test-Konfiguration landet direkt
in der vite.config.ts unter dem test-Schlüssel — keine zweite
Konfigurationsdatei, kein doppeltes TS-Config-Management.
Installation
bash
# Mit npm
npm install -D vitest @vitest/coverage-v8 @vitest/ui jsdom
# Mit pnpm (empfohlen in Monorepos)
pnpm add -D vitest @vitest/coverage-v8 @vitest/ui jsdom
# Claude Code Prompt dafür:
# "Installiere Vitest mit Coverage und UI-Plugin für dieses Projekt"
vite.config.ts — vollständige Test-Konfiguration
typescript
/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
// Globale Test-APIs ohne Import (describe, it, expect, vi)
globals: true,
// DOM-Simulation für React-Tests
environment: 'jsdom',
// Setup-Datei für @testing-library/jest-dom Matcher
setupFiles: ['./src/test/setup.ts'],
// Welche Dateien als Tests erkannt werden
include: [
'src/**/*.{test,spec}.{ts,tsx}',
'src/**/*.{test,spec}.{js,jsx}'
],
// Dateien von der Test-Ausführung ausschließen
exclude: [
'node_modules',
'dist',
'**/*.e2e.{ts,tsx}'
],
// Coverage-Konfiguration
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'lcov', 'html'],
reportsDirectory: './coverage',
include: ['src/**'],
exclude: [
'src/test/**',
'src/**/*.d.ts',
'src/main.tsx'
],
// Mindest-Schwellwerte — CI schlägt fehl wenn unterschritten
thresholds: {
lines: 80,
functions: 80,
branches: 70,
statements: 80
}
},
// Reporter für bessere CI-Ausgabe
reporter: 'verbose',
// Parallelisierung (false = stabiler auf schwachen CI-Rechnern)
pool: 'forks',
poolOptions: {
forks: { singleFork: false }
}
}
})
Setup-Datei für jest-dom
typescript
// src/test/setup.ts
import '@testing-library/jest-dom'
// Globale Mocks die in jedem Test verfügbar sein sollen
import { vi } from 'vitest'
// fetch global mocken (jsdom hat keinen nativen fetch)
import { fetch, Headers, Request, Response } from 'node-fetch'
vi.stubGlobal('fetch', fetch)
vi.stubGlobal('Headers', Headers)
package.json Scripts
json
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"test:ci": "vitest run --reporter=junit --outputFile=test-results.xml"
}
}
01
Installieren
vitest + @vitest/coverage-v8 + jsdom
02
Konfigurieren
test-Block in vite.config.ts
03
Setup-Datei
jest-dom + globale Mocks
04
npm test
Erster Testlauf in <100 ms
02 · Grundlagen
Grundlagen: describe, it, expect
Die Vitest-API ist vollständig Jest-kompatibel. Wer Jest kennt, ist sofort produktiv.
Mit aktivierten globals: true in der Konfiguration entfallen alle Imports
— Claude Code generiert Tests ohne Import-Boilerplate.
Grundstruktur eines Tests
typescript
// src/utils/math.test.ts
// Mit globals:true: kein Import von describe/it/expect nötig!
describe('Math Utilities', () => {
it('addiert zwei positive Zahlen korrekt', () => {
expect(add(2, 3)).toBe(5)
})
it('wirft einen Fehler bei Division durch Null', () => {
expect(() => divide(10, 0)).toThrow('Division durch Null nicht erlaubt')
})
it('verarbeitet negative Zahlen', () => {
expect(add(-5, 3)).toBe(-2)
expect(add(-5, -3)).toBe(-8)
})
})
beforeEach / afterEach / beforeAll / afterAll
typescript
// src/services/userService.test.ts
import { UserService } from './userService'
import { db } from '../db'
describe('UserService', () => {
let service: UserService
beforeAll(async () => {
// Einmalige Initialisierung: DB-Verbindung aufbauen
await db.connect()
})
afterAll(async () => {
await db.disconnect()
})
beforeEach(() => {
// Vor jedem Test: frische Instanz, leere State
service = new UserService()
})
afterEach(async () => {
// Nach jedem Test: Test-Daten bereinigen
await db.query('DELETE FROM users WHERE email LIKE \'%@test.com\'')
})
it('erstellt einen neuen User', async () => {
const user = await service.create({ name: 'Test User', email: 'user@test.com' })
expect(user).toMatchObject({ name: 'Test User', id: expect.any(String) })
})
})
test.each — Parameterized Tests
Parameterized Tests sind einer der produktivsten Aspekte von Vitest. Claude Code kann auf Basis
einer Funktionssignatur automatisch vollständige Testmatrizen generieren — mit Edge Cases,
die Menschen gerne vergessen.
typescript
// Tabellen-Syntax (empfohlen für Lesbarkeit)
test.each([
['hello world', 'Hello World'],
['foo bar baz', 'Foo Bar Baz'],
['', '' ],
['ALREADY CAPS', 'Already Caps'],
])('capitalize(%s) === %s', (input, expected) => {
expect(capitalize(input)).toBe(expected)
})
// Objekt-Syntax mit Template-Literal-Beschreibung
test.each([
{ a: 1, b: 2, expected: 3 },
{ a: -1, b: 1, expected: 0 },
{ a: 0, b: 0, expected: 0 },
])('add($a, $b) = $expected', ({ a, b, expected }) => {
expect(add(a, b)).toBe(expected)
})
// describe.each für ganze Test-Gruppen
describe.each([
['production', { debug: false, cache: true }],
['development', { debug: true, cache: false }],
])('Config in %s mode', (env, config) => {
it('hat korrekten debug-Wert', () => {
expect(getConfig(env).debug).toBe(config.debug)
})
})
Claude Code Prompt für test.each
"Generiere einen parametrisierten Vitest-Test für die Funktion `validateEmail(email: string): boolean` mit mindestens 10 Testfällen — gültige Emails, ungültige, Edge Cases wie leere Strings und internationale Domains."
03 · Mocking
Mocking: vi.mock, vi.fn, vi.spyOn
Vitest's vi-Objekt ist das Äquivalent zu Jest's jest-Objekt und bietet
identische Mocking-Fähigkeiten — aber deutlich schneller und mit besserer TypeScript-Integration.
Claude Code kann komplexe Mock-Setups aus einfachen Beschreibungen generieren.
vi.mock — Modul-Mocking
typescript
// WICHTIG: vi.mock-Aufrufe werden automatisch gehoisted (ans Datei-Anfang)!
// Das ist Vitest's automatisches Hoisting — genau wie bei Jest
vi.mock('../api/userApi', () => ({
fetchUser: vi.fn().mockResolvedValue({
id: '123',
name: 'Max Mustermann',
email: 'max@example.com'
}),
updateUser: vi.fn().mockResolvedValue({ success: true })
}))
// Teilweises Mocking — nur einzelne Exports ersetzen
vi.mock('../utils/dateUtils', async (importOriginal) => {
const original = await importOriginal<typeof import('../utils/dateUtils')>()
return {
...original, // alle echten Exporte behalten
getNow: vi.fn().mockReturnValue(new Date('2026-05-05T12:00:00Z'))
}
})
vi.fn — Mock-Funktionen
typescript
describe('vi.fn Beispiele', () => {
it('trackt Aufrufe und Argumente', () => {
const mockFn = vi.fn()
mockFn('hello', 42)
mockFn('world')
expect(mockFn).toHaveBeenCalledTimes(2)
expect(mockFn).toHaveBeenCalledWith('hello', 42)
expect(mockFn).toHaveBeenLastCalledWith('world')
})
it('gibt definierte Werte zurück', () => {
const mockFn = vi.fn()
.mockReturnValueOnce('first') // Erster Aufruf
.mockReturnValueOnce('second') // Zweiter Aufruf
.mockReturnValue('default') // Alle weiteren
expect(mockFn()).toBe('first')
expect(mockFn()).toBe('second')
expect(mockFn()).toBe('default')
expect(mockFn()).toBe('default')
})
it('simuliert asynchrone Operationen', async () => {
const fetchData = vi.fn()
.mockResolvedValueOnce({ data: [1, 2, 3] })
.mockRejectedValueOnce(new Error('Netzwerkfehler'))
const result = await fetchData()
expect(result.data).toHaveLength(3)
await expect(fetchData()).rejects.toThrow('Netzwerkfehler')
})
})
vi.spyOn — Spies auf echte Objekte
typescript
describe('vi.spyOn', () => {
afterEach(() => {
vi.restoreAllMocks() // Immer nach dem Test aufräumen!
})
it('überwacht console.error ohne es zu unterdrücken', () => {
const consoleSpy = vi.spyOn(console, 'error')
myFunctionThatMightLogError()
expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining('Validation failed')
)
})
it('spioniert auf localStorage', () => {
const setItemSpy = vi.spyOn(Storage.prototype, 'setItem')
saveUserPreference('theme', 'dark')
expect(setItemSpy).toHaveBeenCalledWith('theme', 'dark')
})
it('mockt Date.now() für deterministischen Zeitstempel', () => {
vi.spyOn(Date, 'now').mockReturnValue(1746446400000) // 2026-05-05
const timestamp = createTimestamp()
expect(timestamp).toBe('2026-05-05T12:00:00.000Z')
})
})
// vi.mocked — TypeScript-sichere Mock-Casts
import { fetchUser } from '../api/userApi'
vi.mock('../api/userApi')
it('setzt Rückgabewert type-sicher', () => {
vi.mocked(fetchUser).mockResolvedValue({
id: '42',
name: 'Ada Lovelace',
email: 'ada@code.io'
})
// TypeScript weiß: das ist ein Mock — kein any-Cast nötig
})
vi.stubGlobal — Browser-APIs mocken
typescript
// ResizeObserver, IntersectionObserver, matchMedia — alle nicht in jsdom
beforeEach(() => {
vi.stubGlobal('ResizeObserver', vi.fn(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn()
})))
vi.stubGlobal('matchMedia', vi.fn(query => ({
matches: query === '(prefers-color-scheme: dark)',
addEventListener: vi.fn(),
removeEventListener: vi.fn()
})))
})
afterEach(() => {
vi.unstubAllGlobals()
})
Wichtig: vi.mock Hoisting
Vitest hoisted vi.mock()-Aufrufe automatisch an den Anfang der Datei — genau wie Jest. Das bedeutet: Variablen die im Modul-Scope definiert wurden, sind bei vi.mock noch nicht verfügbar. Nutze vi.hoisted() wenn du Mock-Factories mit Variablen brauchst.
04 · React Testing Library
React Testing Library Integration
React Testing Library und Vitest sind das Dream-Team für Component-Tests 2026.
Mit @testing-library/user-event simulierst du echte Nutzerinteraktionen,
waitFor handhabt async State-Updates, und screen macht Tests
lesbar wie Prosa.
Installation
bash
npm install -D @testing-library/react @testing-library/user-event @testing-library/jest-dom
Einfacher Component-Test
tsx
// src/components/Button.test.tsx
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Button } from './Button'
describe('Button Komponente', () => {
it('rendert mit korrektem Label', () => {
render(<Button label="Klick mich" />)
expect(screen.getByRole('button', { name: 'Klick mich' })).toBeInTheDocument()
})
it('ruft onClick Handler auf', async () => {
const handleClick = vi.fn()
const user = userEvent.setup()
render(<Button label="Test" onClick={handleClick} />)
await user.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledOnce()
})
it('ist deaktiviert wenn disabled prop gesetzt', () => {
render(<Button label="Gesperrt" disabled />)
expect(screen.getByRole('button')).toBeDisabled()
})
})
Formular-Test mit userEvent
tsx
// src/components/LoginForm.test.tsx
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { LoginForm } from './LoginForm'
describe('LoginForm', () => {
const setup = () => {
const user = userEvent.setup()
const onSubmit = vi.fn()
render(<LoginForm onSubmit={onSubmit} />)
return { user, onSubmit }
}
it('submits with valid credentials', async () => {
const { user, onSubmit } = setup()
await user.type(
screen.getByLabelText(/email/i),
'max@example.com'
)
await user.type(
screen.getByLabelText(/passwort/i),
'sicheresPasswort123!'
)
await user.click(screen.getByRole('button', { name: /anmelden/i }))
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith({
email: 'max@example.com',
password: 'sicheresPasswort123!'
})
})
})
it('zeigt Validierungsfehler bei leerem Formular', async () => {
const { user } = setup()
await user.click(screen.getByRole('button', { name: /anmelden/i }))
expect(await screen.findByText(/email ist erforderlich/i)).toBeVisible()
})
it('zeigt Lade-Spinner während Login', async () => {
const { user, onSubmit } = setup()
onSubmit.mockImplementation(
() => new Promise(r => setTimeout(r, 100))
)
await user.type(screen.getByLabelText(/email/i), 'a@b.com')
await user.click(screen.getByRole('button', { name: /anmelden/i }))
expect(screen.getByTestId('loading-spinner')).toBeVisible()
await waitFor(() => expect(screen.queryByTestId('loading-spinner')).toBeNull())
})
})
Custom Render-Wrapper mit Context/Provider
tsx
// src/test/utils.tsx — wiederverwendbarer Render-Wrapper
import { render, RenderOptions } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { MemoryRouter } from 'react-router-dom'
const createTestQueryClient = () => new QueryClient({
defaultOptions: { queries: { retry: false, gcTime: 0 } }
})
export function renderWithProviders(
ui: React.ReactElement,
{ route = '/', ...options }: { route?: string } & RenderOptions = {}
) {
const queryClient = createTestQueryClient()
function Wrapper({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={[route]}>
{children}
</MemoryRouter>
</QueryClientProvider>
)
}
return { ...render(ui, { wrapper: Wrapper, ...options }), queryClient }
}
getBy vs queryBy vs findBy — die Unterschiede
getBy: wirft wenn nicht gefunden (synchron) — für Elemente die immer da sein müssen. queryBy: gibt null zurück wenn nicht gefunden — für "ist NICHT da"-Checks. findBy: gibt Promise zurück — für Elemente die erst nach async-Operationen erscheinen (intern waitFor).
05 · Coverage
Coverage: v8, istanbul, lcov für CI
Vitest unterstützt zwei Coverage-Provider: v8 (schneller, nutzt V8's nativen
Coverage-Mechanismus) und istanbul (mehr Konfigurationsoptionen, breiter
unterstützt). Für die meisten Projekte ist v8 die bessere Wahl.
Provider-Vergleich
| Kriterium |
v8 |
istanbul |
| Performance |
✓ schneller |
∅ etwas langsamer |
| Genauigkeit |
∅ gut |
✓ präziser |
| Source Maps |
✓ nativ |
✓ ja |
| Extra-Package |
@vitest/coverage-v8 |
@vitest/coverage-istanbul |
| Empfehlung |
✓ Standard 2026 |
∅ Legacy/spezielle Needs |
Vollständige Coverage-Konfiguration
typescript
// vite.config.ts — coverage-Block
coverage: {
provider: 'v8',
// Reporter: text (Terminal), json (CI), lcov (Codecov/SonarQube), html (Browser)
reporter: ['text', 'text-summary', 'json', 'lcov', 'html'],
// Output-Verzeichnis
reportsDirectory: './coverage',
// Was gemessen wird
include: ['src/**/*.{ts,tsx}'],
exclude: [
'src/**/*.d.ts',
'src/test/**',
'src/main.tsx',
'src/**/*.stories.{ts,tsx}',
'src/**/__mocks__/**'
],
// Schwellwerte: CI schlägt bei Unterschreitung fehl
thresholds: {
// Global-Schwellwerte
lines: 80,
functions: 80,
branches: 70,
statements: 80,
// Per-File-Schwellwerte für kritische Dateien
perFile: true,
// Auto-Update: Schlägt fehl wenn Coverage SINKT (ratchet)
autoUpdate: false // true = schreibt neue Schwellwerte nach Coverage-Erhöhung
},
// Alle Quelldateien einschließen, auch ohne Tests
all: true,
// Branch-Coverage aktivieren (v8-spezifisch)
clean: true // Coverage-Verzeichnis vor jedem Run löschen
}
GitHub Actions CI-Integration
yaml
# .github/workflows/test.yml
name: Tests & Coverage
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Tests with Coverage
run: npm run test:coverage
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: ./coverage/lcov.info
fail_ci_if_error: true
- name: Upload Coverage HTML Report
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: coverage/
Coverage-Kommentare in Code ausschließen
typescript
// Einzelne Zeile ausschließen:
const impossibleBranch = true /* c8 ignore next */
// Block ausschließen:
/* c8 ignore start */
if (process.env.NODE_ENV === 'development') {
console.debug('Debug-only Code')
}
/* c8 ignore stop */
// Funktion komplett ausschließen:
function onlyForManualTesting() { /* c8 ignore next */
// ...
}
Claude Code Coverage-Analyse
"Analysiere meine Coverage-Lücken in src/services/ und schreibe Tests für die Funktionen mit unter 70% Branch Coverage." Claude liest den HTML-Report, identifiziert die Lücken und generiert gezielt Tests für ungetestete Branches — inklusive Error-Pfade.
06 · Vitest UI & Watch Mode
Vitest UI und Watch Mode
Vitest bietet drei Entwicklungsmodi: den klassischen Watch Mode der Tests
bei Dateiänderungen neu ausführt, das grafische Vitest UI als Browser-basierte
Test-IDE, und den experimentellen Browser Mode der Tests im echten Browser laufen lässt.
Watch Mode — HMR-basiertes Neuausführen
bash
# Startet Watch Mode (Standard wenn keine --run Flag)
npx vitest
# Nur bestimmte Dateien watchen
npx vitest src/components/
# Mit Pattern-Filter
npx vitest --reporter=verbose --testNamePattern="Button"
# Nur geänderte Dateien (wie git diff)
npx vitest --changed
# Watch Mode Keyboard-Shortcuts (interaktiv):
# f → Fehlgeschlagene Tests nochmal ausführen
# a → Alle Tests ausführen
# p → Datei-Pattern filtern
# t → Test-Name-Pattern filtern
# q → Beenden
Vitest UI — Browser-basierte Test-IDE
bash
# @vitest/ui installieren (einmalig)
npm install -D @vitest/ui
# UI starten (öffnet automatisch http://localhost:51204/__vitest__/)
npx vitest --ui
# Mit Coverage im UI
npx vitest --ui --coverage
Das Vitest UI bietet eine vollständige Test-Übersicht im Browser: Datei-Baum,
Test-Ergebnisse, Fehler-Stack-Traces, Coverage-Visualisierung und einen interaktiven
Module-Graph. Besonders hilfreich beim Debuggen komplexer Mock-Setups.
vite.config.ts UI-Konfiguration
typescript
test: {
// UI-spezifische Einstellungen
ui: true,
open: true, // Browser automatisch öffnen
// API-Port für UI anpassen
uiBase: '/__vitest__/',
// HTML-Reporter für statische Reports
reporters: ['default', 'html'],
outputFile: {
html: './test-results/index.html'
}
}
Browser Mode (experimentell)
bash
npm install -D @vitest/browser playwright
typescript
// vite.config.ts — Browser Mode
test: {
browser: {
enabled: true,
name: 'chromium',
provider: 'playwright',
headless: true,
// Nur bestimmte Test-Dateien im Browser ausführen
include: ['src/**/*.browser.test.{ts,tsx}']
}
}
// Browser-Test-Datei mit page-Utilities
import { page } from '@vitest/browser/context'
test('zeigt Inhalt nach Click', async () => {
render(<MyComponent />)
await page.getByRole('button').click()
await expect.element(page.getByText('Inhalt geladen')).toBeVisible()
})
Snapshot-Tests mit Vitest
tsx
// Inline Snapshots — direkt im Test-File gespeichert
it('rendert Card korrekt', () => {
const { container } = render(<Card title="Test" content="Inhalt" />)
expect(container.firstChild).toMatchSnapshot()
})
// Inline Snapshot (wird automatisch befüllt beim ersten Run)
it('serialisiert User-Objekt', () => {
const user = { id: '1', name: 'Max', role: 'admin' }
expect(user).toMatchInlineSnapshot(`
{
"id": "1",
"name": "Max",
"role": "admin",
}
`)
})
// Snapshots aktualisieren:
// npx vitest run --update-snapshots (oder: -u)
--ui
Vitest UI
Browser-IDE mit Module Graph und Coverage
--watch
Watch Mode
HMR-basiert, nur geänderte Tests
--browser
Browser Mode
Tests im echten Chromium/Firefox
-u
Update Snapshots
Alle Snapshots neu generieren
Browser Mode in CI
Der Browser Mode benötigt Playwright als Dependency und einen installierten Browser. In GitHub Actions: npx playwright install --with-deps chromium VOR dem Test-Run ausführen. Browser Mode ist für Tests die echte DOM-APIs wie WebGL, Web Audio oder CSS Animations brauchen — für normale React-Tests reicht jsdom.
Vitest-Tests mit KI schreiben
Claude Code analysiert dein Projekt, erkennt Testing-Patterns und generiert vollständige
Test-Suites — inklusive Mocks, Edge Cases und Coverage für kritische Pfade.
Teste es selbst kostenlos.
Kostenlos mit Claude Code starten →
Zusammenfassung
Das Vitest-Setup 2026 auf einen Blick
- Setup:
test-Block in vite.config.ts, globals: true, jsdom oder node Environment
- Grundlagen: Jest-kompatible API —
describe/it/expect, Lifecycle-Hooks, test.each für Parameterized Tests
- Mocking:
vi.mock mit automatischem Hoisting, vi.fn für Mock-Funktionen, vi.spyOn für Spies, vi.stubGlobal für Browser-APIs
- React Testing Library:
render/screen/userEvent/waitFor, Custom Render-Wrapper für Provider, getBy/queryBy/findBy je nach Async-Kontext
- Coverage: v8-Provider,
text/json/lcov/html Reporter, Thresholds für CI-Gates, all: true für ungetestete Dateien
- Vitest UI: Browser-basierte Test-IDE mit Module Graph, Watch Mode mit Keyboard-Shortcuts, experimenteller Browser Mode
Der Claude Code Vorteil
Claude Code kennt die gesamte Vitest-API und kann aus einer Funktionssignatur vollständige Test-Suites generieren, fehlgeschlagene Tests debuggen, Mock-Strategies vorschlagen und Coverage-Lücken automatisch schließen — ohne dass du dazu die Dokumentation lesen musst.