Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Testing mit Vitest und React Testing Library in React

Testing mit Vitest und React Testing Library

~17 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Bisher haben wir jede Änderung manuell im Browser geprüft – funktioniert, wird aber bei wachsendem Projekt schnell unpraktikabel: bei JEDER Änderung müssten Sie ALLE bisherigen Features erneut von Hand durchklicken. Automatisierte Tests übernehmen genau das.

Vitest und React Testing Library installieren

Vitest ist ein Test-Runner, der speziell für Vite-Projekte gebaut ist (nutzt dieselbe Konfiguration, dieselbe Geschwindigkeit) – das direkte, moderne Äquivalent zum älteren Jest. React Testing Library ("RTL") ist die Bibliothek, mit der man Komponenten so testet, WIE ein echter Nutzer mit ihnen interagieren würde (Klicks, Texteingabe, sichtbarer Text), statt interne Implementierungsdetails zu prüfen.

npm install --save-dev vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdom

vite.config.js um Test-Konfiguration erweitern

vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom', // simuliert einen Browser in Node.js, kein echter Browser nötig
    globals: true,        // erlaubt describe/it/expect ohne Import in jeder Testdatei
    setupFiles: './src/test/setup.js',
  },
});
src/test/setup.js
import '@testing-library/jest-dom';
// erweitert expect() um Matcher wie .toBeInTheDocument(), .toHaveTextContent() usw.

Der erste Test: eine reine Funktion

Wir beginnen mit dem EINFACHSTEN Fall: bmiKategorie() aus der "React Native Referenz"-Serie kennen Sie bereits als Muster – eine reine Funktion (keine Komponente, kein State) ist am leichtesten zu testen. Unser Projekt hat kein BMI-Beispiel, aber filterItemsSlowlys Filterlogik aus SearchDemoPage (Kapitel 33) ist strukturell ähnlich. Testen wir stattdessen etwas aus unserem echten Projekt: die Gesamtsumme-Berechnung aus CartPage, ausgelagert als eigene, testbare Funktion:

src/utils/cartTotal.js
export function berechneGesamtsumme(items) {
  return items.reduce((summe, item) => summe + item.price * item.quantity, 0);
}
src/utils/cartTotal.test.js
import { describe, it, expect } from 'vitest';
import { berechneGesamtsumme } from './cartTotal';

describe('berechneGesamtsumme', () => {
  it('gibt 0 für einen leeren Warenkorb zurück', () => {
    expect(berechneGesamtsumme([])).toBe(0);
  });

  it('berechnet die Summe für einen einzelnen Artikel', () => {
    const items = [{ price: 10, quantity: 2 }];
    expect(berechneGesamtsumme(items)).toBe(20);
  });

  it('summiert mehrere unterschiedliche Artikel korrekt', () => {
    const items = [
      { price: 10, quantity: 2 },
      { price: 5, quantity: 3 },
    ];
    expect(berechneGesamtsumme(items)).toBe(35);
  });
});

describe gruppiert zusammengehörige Tests, it (Alias: test) beschreibt EINEN konkreten Testfall in Alltagssprache, expect(...).toBe(...) ist die Behauptung selbst. Fügen Sie ein Skript in package.json hinzu: "test": "vitest", dann npm test ausführen – Vitest überwacht Dateien automatisch und läuft bei jeder Änderung erneut.

Eine Komponente testen: ProductCard

Komponenten-Tests prüfen NICHT internen State direkt, sondern SICHTBARES Verhalten – GENAU wie ein Nutzer die App erlebt. RTL's render() rendert die Komponente in eine virtuelle DOM-Umgebung, screen findet Elemente darin:

src/components/ProductCard.test.jsx
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ProductCard from './ProductCard';

describe('ProductCard', () => {
  const defaultProps = {
    name: 'Wanderschuhe',
    price: 89.99,
    imageUrl: 'https://example.com/schuhe.jpg',
    onSelect: vi.fn(),
    onAddToCart: vi.fn(),
  };

  it('zeigt Name und Preis an', () => {
    render(<ProductCard {...defaultProps} />);
    expect(screen.getByText('Wanderschuhe')).toBeInTheDocument();
    expect(screen.getByText('$89.99')).toBeInTheDocument();
  });

  it('ruft onAddToCart auf, wenn der Warenkorb-Button geklickt wird', async () => {
    const user = userEvent.setup();
    render(<ProductCard {...defaultProps} />);

    await user.click(screen.getByText('In den Warenkorb'));

    expect(defaultProps.onAddToCart).toHaveBeenCalledOnce();
    expect(defaultProps.onSelect).not.toHaveBeenCalled();
  });

  it('schaltet den Favoriten-Status beim Klick auf das Herz um', async () => {
    const user = userEvent.setup();
    render(<ProductCard {...defaultProps} />);

    expect(screen.getByText('♡')).toBeInTheDocument();
    await user.click(screen.getByText('♡'));
    expect(screen.getByText('♥')).toBeInTheDocument();
  });
});

Die Details, die diesen Test gut machen

  • vi.fn() erzeugt eine "Mock-Funktion" – eine Test-Attrappe, die aufgezeichnet wird, aber selbst nichts tut. toHaveBeenCalledOnce() prüft, dass sie GENAU einmal aufgerufen wurde.
  • screen.getByText(...) sucht nach SICHTBAREM Text – genau das, was ein echter Nutzer sehen würde, nicht nach internen Variablennamen oder CSS-Klassen.
  • expect(defaultProps.onSelect).not.toHaveBeenCalled() im "Add to Cart"-Test verifiziert INDIREKT, dass event.stopPropagation() aus Kapitel 29 tatsächlich funktioniert – GENAU der Bug, den stopPropagation() verhindern soll, würde diesen Test fehlschlagen lassen.
  • userEvent.setup() statt des älteren fireEvent simuliert ECHTE Nutzerinteraktion realistischer (inklusive Zwischenschritten wie Fokussieren vor dem Klicken).

Achtung: Testen Sie NICHT isFavorite als internen State direkt (z. B. über einen Komponenten-Instanz-Zugriff) – RTL bietet das absichtlich NICHT an. Der Test prüft stattdessen das SICHTBARE Ergebnis ( wird zu ) – bleibt der Test auch dann gültig, wenn Sie die interne Implementierung später ändern (z. B. isFavorite in einen Zustand-Store auslagern), solange das sichtbare Verhalten gleich bleibt.

Tipp: Faustregel der React Testing Library, oft zitiert: "The more your tests resemble the way your software is used, the more confidence they can give you." Tests, die Implementierungsdetails prüfen, brechen bei jedem Refactoring, selbst wenn das SICHTBARE Verhalten unverändert bleibt – genau das Gegenteil von dem, was gute Tests leisten sollen.