Skip to Content
Programmieren9 - CodequalitätUnit-Testing mit Vitest

Unit-Testing mit Vitest

Dauer: 45 Minuten

Ziel: Zentrale Logik mit schnellen, verlässlichen Unit-Tests absichern

Was sind Unit-Tests?

Unit-Tests sind automatisierte Tests, die einzelne Funktionen oder Module isoliert prüfen. Sie stellen sicher, dass der Code korrekt funktioniert — auch nach Änderungen.

Vorteile von Unit-Tests:

  • Fehler werden frühzeitig erkannt
  • Änderungen am Code können sicher durchgeführt werden
  • Tests dokumentieren das erwartete Verhalten
  • KI-generierter Code kann systematisch überprüft werden

Für unser TUI-Projekt sind Unit-Tests die wichtigste Teststufe nach Type Checking und Linting.

Es gibt viele Möglichkeiten, Unit-Tests zu schreiben. Im JavaScript-/TypeScript-Umfeld gibt es vor allem zwei gängige Frameworks:

Wir verwenden Vitest  — für unser TUI-Projekt mit TypeScript sprechen mehrere Gründe dafür:

  • Erstklassiger TypeScript-Support — Vitest versteht TypeScript ohne zusätzliche Konfiguration. Jest benötigt dafür extra Pakete wie ts-jest oder @swc/jest, was im Workshop unnötige Komplexität bedeutet.
  • Schnelle Ausführung — Vitest nutzt Vite  unter der Haube und ist dadurch deutlich schneller als Jest.
  • Jest-kompatible APIdescribe, it, expect funktionieren wie bei Jest. Das Wissen ist direkt übertragbar und in vielen Projekten und Webapps verbreitet.
  • Einfaches Setupnpm install -D vitest und ein Script in package.json reichen aus.

Node.js bringt seit Version 18 einen eingebauten Test Runner mit (node:test), der ohne zusätzliche Pakete funktioniert und für einfache Setups völlig ausreicht. Die Konzepte sind dabei dieselben: Tests werden mit describe und it strukturiert, und Erwartungen mit Assertions geprüft. Wer node:test kennt, findet sich in Vitest sofort zurecht — und umgekehrt. Mehr dazu in der optionalen Lektion zu node:test.

Setup mit Vitest

npm install -D vitest

In package.json:

{ "scripts": { "test": "vitest run", "test:watch": "vitest" } }

Beispiel: Reine Funktion testen

Vitest-Tests nutzen drei zentrale Funktionen:

FunktionBedeutung
describe('...', () => {})Gruppiert zusammengehörige Tests
it('...', () => {})Definiert einen einzelnen Testfall
expect(wert)Startet eine Prüfung (Assertion), z.B. expect(wert).toBe(42)

Beispiel mit TypeScript-Dateien:

src/temperature.ts:

export function celsiusToFahrenheit(celsius: number): number { // Defensiver Check: zur Laufzeit relevant, falls die Funktion // aus ungetyptem JavaScript aufgerufen wird if (typeof celsius !== 'number' || Number.isNaN(celsius)) { throw new Error('Ungültige Temperatur'); } return (celsius * 9) / 5 + 32; }

src/temperature.test.ts:

import { describe, expect, it } from 'vitest'; import { celsiusToFahrenheit } from './temperature.ts'; describe('celsiusToFahrenheit', () => { it('rechnet 0°C in 32°F um', () => { // Arrange — Testdaten vorbereiten const celsius = 0; // Act — Funktion aufrufen const result = celsiusToFahrenheit(celsius); // Assert — Ergebnis prüfen expect(result).toBe(32); }); it('rechnet 100°C in 212°F um', () => { expect(celsiusToFahrenheit(100)).toBe(212); }); it('wirft Fehler bei NaN-Eingabe', () => { expect(() => celsiusToFahrenheit(Number.NaN)).toThrow('Ungültige Temperatur'); }); });

Der erste Test zeigt das AAA-Schema ausführlich mit Kommentaren. Bei einfachen Fällen (zweiter und dritter Test) darf alles in einer Zeile stehen.

Tests ausführen

Tests ausführen mit npm test (Kurzform von npm run test):

npm test

Beispielausgabe (kann je nach Umgebung leicht abweichen):

✓ src/temperature.test.ts (3 tests) 5ms ✓ celsiusToFahrenheit > rechnet 0°C in 32°F um ✓ celsiusToFahrenheit > rechnet 100°C in 212°F um ✓ celsiusToFahrenheit > wirft Fehler bei NaN-Eingabe Test Files 1 passed (1) Tests 3 passed (3)

Watch Mode

Mit npm run test:watch startet Vitest im Watch-Modus. Tests laufen dann bei Dateiänderungen automatisch neu.

Das ist praktisch während der Entwicklung. Für einen einmaligen Check (z. B. vor Commit) nutzen wir npm test.

Beenden: q oder Ctrl+C.

Wichtige expect-Methoden

MethodeBeschreibung
expect(a).toBe(b)Prüft ob a === b
expect(a).not.toBe(b)Prüft ob a !== b
expect(a).toEqual(b)Prüft ob Objekte/Arrays inhaltlich gleich sind
expect(fn).toThrow()Prüft ob fn einen Fehler wirft (optional mit erwarteter Nachricht, z.B. toThrow('msg'))
expect(a).toBeTruthy()Prüft ob a truthy ist
expect(a).toContain(b)Prüft ob Array/String b enthält

Dateikonvention

Testdateien werden neben der getesteten Datei abgelegt und erhalten die Endung .test.js (bzw. .test.ts mit TypeScript):

src/ temperature.ts temperature.test.ts weather-api.ts weather-api.test.ts

Gute Praxis für Unit-Tests

  • Testnamen klar formulieren (erwartetes Verhalten nennen)
  • AAA-Schema verwenden: Arrange (Testdaten vorbereiten), Act (Funktion aufrufen), Assert (Ergebnis prüfen)
  • Nur eine konkrete Erwartung pro Verhalten testen
  • Vor allem pure Funktionen testen (ohne I/O, ohne globale Seiteneffekte)
  • Verhalten und Anforderungen testen, nicht nur Implementierungsdetails
  • KI-generierte Tests kritisch prüfen (decken sie echte Risiken oder nur den Happy Path ab?)

Tipp: KI-Coding-Agents im Test-Workflow

  • KI-Coding-Agents können Tests direkt ausführen (z. B. npm run test oder npm run test:watch).
  • Sie können auch Testabdeckung prüfen (z. B. npx vitest run --coverage, wenn Coverage im Projekt eingerichtet ist).
  • Sie können Tests generieren — entweder explizit auf Prompt oder als vorgegebene Teilaufgabe im Workflow (z. B. vor Commit).

TDD (Test-Driven Development)

Eine verbreitete Technik: zuerst einen fehlschlagenden Test schreiben, dann den minimalen Code, der den Test bestehen lässt, anschließend refactoren. So entstehen Tests, die echte Anforderungen prüfen.

Als nächster Schritt: ein konkreter Integrationscheck im TUI mit 1 Erfolgs- und 1 Fehlerfall.

Die weiteren Qualitätsstufen (u. a. E2E, CI/CD) folgen im Ausblick.