Back

Unit- vs. Integrationstests in JavaScript: Wann welche Teststrategie einsetzen?

Unit- vs. Integrationstests in JavaScript: Wann welche Teststrategie einsetzen?

Jeder JavaScript-Entwickler steht vor derselben Frage beim Aufbau zuverlässiger Anwendungen: Sollte man mehr Unit-Tests oder Integrationstests schreiben? Wählt man falsch, verschwendet man entweder Stunden mit dem Debugging fragiler Tests oder liefert Bugs in die Produktivumgebung aus. Die Antwort liegt nicht darin, sich für eine der beiden Optionen zu entscheiden – es geht darum zu verstehen, wann welcher Testtyp den größten Mehrwert liefert.

Dieser Artikel verdeutlicht die Unterschiede zwischen Unit- und Integrationstests in JavaScript, zeigt praktische Beispiele für beide Ansätze und bietet einen Entscheidungsrahmen für die ausgewogene Balance beider Strategien in Ihrer Teststrategie.

Wichtigste Erkenntnisse

  • Unit-Tests verifizieren isolierte Codeabschnitte für Geschwindigkeit und Präzision
  • Integrationstests validieren Komponenteninteraktionen für realitätsnahe Sicherheit
  • Eine 70-20-10-Verteilung (Unit-Integration-E2E) funktioniert für die meisten JavaScript-Projekte
  • Die Wahl hängt davon ab, was getestet wird: Algorithmen benötigen Unit-Tests, Workflows benötigen Integrationstests

Was sind Unit-Tests?

Unit-Tests verifizieren, dass einzelne Codeabschnitte isoliert korrekt funktionieren. Man kann es sich wie das Testen eines einzelnen LEGO-Steins vorstellen, bevor man ihn zur Konstruktion hinzufügt.

In JavaScript konzentriert sich ein Unit-Test typischerweise auf:

  • Eine einzelne Funktion oder Methode
  • Eine Komponente ohne ihre Kindkomponenten
  • Eine bestimmte Klasse oder ein Modul

Unit-Test-Beispiel

// calculator.js
export function calculateDiscount(price, percentage) {
  if (percentage < 0 || percentage > 100) {
    throw new Error('Invalid percentage');
  }
  return price * (1 - percentage / 100);
}

// calculator.test.js
import { calculateDiscount } from './calculator';

test('applies 20% discount correctly', () => {
  expect(calculateDiscount(100, 20)).toBe(80);
});

test('throws error for invalid percentage', () => {
  expect(() => calculateDiscount(100, 150)).toThrow('Invalid percentage');
});

Unit-Tests glänzen durch:

  • Geschwindigkeit: Millisekunden pro Test, da keine I/O-Operationen oder externe Abhängigkeiten vorhanden sind
  • Präzision: Lokalisiert exakte Fehlerstellen
  • Stabilität: Brechen selten aufgrund nicht zusammenhängender Änderungen

Was sind Integrationstests?

Integrationstests verifizieren, dass mehrere Teile Ihrer Anwendung korrekt zusammenarbeiten. Anstatt den LEGO-Stein allein zu testen, testet man, wie mehrere Steine miteinander verbunden werden.

Integrationstests in JavaScript decken typischerweise ab:

  • Komponenteninteraktionen mit APIs
  • Mehrere Module, die zusammenarbeiten
  • Datenbankoperationen mit Geschäftslogik
  • UI-Komponenten mit State Management

Integrationstest-Beispiel

// userProfile.test.js
import { render, screen, waitFor } from '@testing-library/react';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import UserProfile from './UserProfile';

const server = setupServer(
  rest.get('/api/user/:id', (req, res, ctx) => {
    return res(ctx.json({ 
      id: req.params.id, 
      name: 'Jane Doe',
      role: 'Developer' 
    }));
  })
);

beforeAll(() => server.listen());
afterAll(() => server.close());

test('displays user data after loading', async () => {
  render(<UserProfile userId="123" />);
  
  expect(screen.getByText(/loading/i)).toBeInTheDocument();
  
  await waitFor(() => {
    expect(screen.getByText('Jane Doe')).toBeInTheDocument();
    expect(screen.getByText('Developer')).toBeInTheDocument();
  });
});

Integrationstests bieten:

  • Vertrauen: Validiert reale Benutzer-Workflows
  • Abdeckung: Testet Interaktionen zwischen Komponenten
  • Realitätsnähe: Fängt Probleme auf, die Unit-Tests übersehen

Wesentliche Unterschiede, die zählen

Umfang und Isolation

Unit-Tests isolieren Code mithilfe von Mocks und Stubs. Man kontrolliert jede Variable. Integrationstests verwenden wo möglich echte Implementierungen und mocken nur externe Grenzen wie APIs oder Datenbanken.

Ausführungsgeschwindigkeit

Unit-Tests laufen in 1-50ms pro Test. Man kann Tausende in Sekunden ausführen. Integrationstests benötigen 100-500ms oder mehr. Sie beinhalten Setup, Teardown und manchmal echte I/O-Operationen.

Wartungsaufwand

Unit-Tests brechen nur, wenn sich ihre spezifische Unit ändert. Integrationstests können durch Änderungen überall im getesteten Ablauf brechen und erfordern mehr Untersuchungszeit.

Fehlererkennung

Unit-Tests fangen Logikfehler und Grenzfälle in isoliertem Code ab. Integrationstests fangen Verkabelungsprobleme, falsche Annahmen und Vertragsverletzungen zwischen Komponenten ab.

Wann welchen Testtyp verwenden

Unit-Tests schreiben für:

  • Pure Functions: Geschäftslogik, Berechnungen, Datentransformationen
  • Komplexe Algorithmen: Sortierung, Suche, Validierungsregeln
  • Grenzfälle: Fehlerbehandlung, Randbedingungen
  • Hilfsfunktionen: Formatierer, Parser, Helper

Integrationstests schreiben für:

  • API-Interaktionen: HTTP-Requests, Response-Handling
  • Benutzer-Workflows: Mehrstufige Prozesse, Formularübermittlungen
  • Komponentenintegration: Eltern-Kind-Komponentenkommunikation
  • State Management: Redux Actions, Context API Flows
  • Datenbankoperationen: CRUD-Operationen mit Geschäftslogik

Die praktische Teststrategie

Die meisten erfolgreichen JavaScript-Projekte folgen einer 70-20-10-Verteilung:

  • 70% Unit-Tests: Schnelles Feedback, einfaches Debugging
  • 20% Integrationstests: Vertrauen in Komponenteninteraktionen
  • 10% End-to-End-Tests: Finale Validierung kritischer Pfade

Dies ist keine starre Regel – passen Sie sie basierend auf Ihrem Anwendungstyp an. API-lastige Anwendungen benötigen mehr Integrationstests. Algorithmus-lastige Bibliotheken benötigen mehr Unit-Tests.

Integration in die CI/CD-Pipeline

Strukturieren Sie Ihre Pipeline für schnelles Feedback:

  1. Pre-commit: Unit-Tests ausführen (< 5 Sekunden)
  2. Pull Request: Alle Unit- und Integrationstests ausführen
  3. Pre-deploy: Vollständige Test-Suite inklusive E2E-Tests ausführen

Tools wie Jest für Unit-Tests, Testing Library für Integrationstests und MSW für API-Mocking machen diese Pipeline effizient und wartbar.

Häufige Fallstricke vermeiden

  1. Übermäßiges Mocking in Integrationstests: Konterkariert den Zweck des Testens von Interaktionen
  2. Testen von Implementierungsdetails: Fokus auf Verhalten, nicht auf interne Struktur
  3. Ignorieren der Testgeschwindigkeit: Langsame Tests entmutigen häufiges Ausführen
  4. Tests erst nach Bugs schreiben: Proaktives Testen verhindert Probleme

Fazit

Unit- und Integrationstests in JavaScript sind keine konkurrierenden Strategien – sie sind komplementäre Werkzeuge. Unit-Tests bieten Geschwindigkeit und Präzision für isolierte Logik. Integrationstests geben Vertrauen, dass Komponenten korrekt zusammenarbeiten.

Beginnen Sie mit Unit-Tests für Geschäftslogik und Pure Functions. Fügen Sie Integrationstests für kritische Benutzerpfade und Komponenteninteraktionen hinzu. Überspringen Sie religiöse Debatten über Testphilosophien und konzentrieren Sie sich darauf, was Ihnen Vertrauen gibt, Code auszuliefern.

Die beste JavaScript-Teststrategie ist diejenige, die Bugs abfängt, bevor Benutzer sie entdecken, während sie gleichzeitig Ihre Entwicklungsgeschwindigkeit hochhält. Balancieren Sie beide Typen basierend auf den Anforderungen Ihrer Anwendung und passen Sie an, während Sie lernen, was am häufigsten bricht.

Häufig gestellte Fragen

Mocken Sie immer externe Abhängigkeiten in Unit-Tests. Dazu gehören Datenbanken, APIs, Dateisysteme und andere Services. Mocking stellt sicher, dass Tests schnell laufen, vorhersagbar bleiben und wirklich Ihren Code isoliert testen, anstatt das Verhalten externer Systeme.

Beginnen Sie mit der Frage, was kaputtgehen könnte. Wenn die Logik selbst komplex ist, schreiben Sie zuerst Unit-Tests. Wenn das Feature mehrere Komponenten umfasst, die miteinander oder mit externen Services kommunizieren, priorisieren Sie Integrationstests. Die meisten Features profitieren von beiden Typen.

Unit-Tests sollten für die gesamte Suite in unter 10 Sekunden abgeschlossen sein. Integrationstests können 1-5 Minuten dauern. Wenn Ihre Tests länger dauern, teilen Sie sie in parallele Jobs auf oder identifizieren Sie langsame Tests, die optimiert werden müssen. Schnelle Tests ermutigen Entwickler, sie häufig auszuführen.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay